fix(forms): changed forms to create only one value accessor instead of always creating DefaultValueAccessor

This commit is contained in:
vsavkin 2015-05-19 21:10:23 -07:00
parent 2ff3873881
commit 30c3e5a84e
3 changed files with 102 additions and 46 deletions

View File

@ -32,35 +32,6 @@ function _lookupControl(groupDirective: ControlGroupDirective, controlOrName: an
return control; return control;
} }
/**
* The default accessor for writing a value and listening to changes that is used by a {@link
* Control} directive.
*
* This is the default strategy that Angular uses when no other accessor is applied.
*
* # Example
* ```
* <input type="text" [control]="loginControl">
* ```
*
* @exportedAs angular2/forms
*/
@Directive({
selector: '[control]',
hostListeners:
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
hostProperties: {'value': 'value'}
})
export class DefaultValueAccessor {
value;
onChange: Function;
constructor() { this.onChange = (_) => {}; }
writeValue(value) { this.value = value }
}
/** /**
* Binds a control group to a DOM element. * Binds a control group to a DOM element.
* *
@ -171,11 +142,9 @@ export class ControlDirective {
validator: Function; validator: Function;
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective, constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective) {
valueAccessor: DefaultValueAccessor) {
this._groupDirective = groupDirective; this._groupDirective = groupDirective;
this._controlOrName = null; this._controlOrName = null;
this.valueAccessor = valueAccessor;
this.validator = Validators.nullValidator; this.validator = Validators.nullValidator;
} }
@ -189,6 +158,10 @@ export class ControlDirective {
var c = this._control(); var c = this._control();
c.validator = Validators.compose([c.validator, this.validator]); c.validator = Validators.compose([c.validator, this.validator]);
if (isBlank(this.valueAccessor)) {
throw new BaseException(`Cannot find value accessor for control "${controlOrName}"`);
}
this._updateDomValue(); this._updateDomValue();
this._setUpUpdateControlValue(); this._setUpUpdateControlValue();
} }
@ -202,6 +175,75 @@ export class ControlDirective {
_control() { return _lookupControl(this._groupDirective, this._controlOrName); } _control() { return _lookupControl(this._groupDirective, this._controlOrName); }
} }
/**
* The default accessor for writing a value and listening to changes that is used by a {@link
* Control} directive.
*
* This is the default strategy that Angular uses when no other accessor is applied.
*
* # Example
* ```
* <input type="text" [control]="loginControl">
* ```
*
* @exportedAs angular2/forms
*/
@Directive({
selector: 'input:not([type=checkbox])[control],textarea[control]',
hostListeners:
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
hostProperties: {'value': 'value'}
})
export class DefaultValueAccessor {
value = null;
onChange: Function;
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
this.onChange = (_) => {};
cd.valueAccessor = this;
}
writeValue(value) {
this._renderer.setElementProperty(this._elementRef.parentView.render,
this._elementRef.boundElementIndex, 'value', value)
}
}
/**
* The accessor for writing a value and listening to changes that is used by a {@link
* Control} directive.
*
* This is the default strategy that Angular uses when no other accessor is applied.
*
* # Example
* ```
* <input type="text" [control]="loginControl">
* ```
*
* @exportedAs angular2/forms
*/
@Directive({
selector: 'select[control]',
hostListeners:
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
hostProperties: {'value': 'value'}
})
export class SelectControlValueAccessor {
value = null;
onChange: Function;
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
this.onChange = (_) => {};
this.value = '';
cd.valueAccessor = this;
}
writeValue(value) {
this._renderer.setElementProperty(this._elementRef.parentView.render,
this._elementRef.boundElementIndex, 'value', value)
}
}
/** /**
* The accessor for writing a value and listening to changes on a checkbox input element. * The accessor for writing a value and listening to changes on a checkbox input element.
* *
@ -219,17 +261,12 @@ export class ControlDirective {
hostProperties: {'checked': 'checked'} hostProperties: {'checked': 'checked'}
}) })
export class CheckboxControlValueAccessor { export class CheckboxControlValueAccessor {
_elementRef: ElementRef;
_renderer: Renderer;
checked: boolean; checked: boolean;
onChange: Function; onChange: Function;
constructor(cd: ControlDirective, elementRef: ElementRef, renderer: Renderer) { constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
this.onChange = (_) => {}; this.onChange = (_) => {};
this._elementRef = elementRef; cd.valueAccessor = this;
this._renderer = renderer;
cd.valueAccessor = this; // ControlDirective should inject CheckboxControlDirective
} }
writeValue(value) { writeValue(value) {
@ -246,5 +283,10 @@ export class CheckboxControlValueAccessor {
* *
* @exportedAs angular2/forms * @exportedAs angular2/forms
*/ */
export const formDirectives: List<Type> = CONST_EXPR( export const formDirectives: List<Type> = CONST_EXPR([
[ControlGroupDirective, ControlDirective, CheckboxControlValueAccessor, DefaultValueAccessor]); ControlGroupDirective,
ControlDirective,
CheckboxControlValueAccessor,
DefaultValueAccessor,
SelectControlValueAccessor
]);

View File

@ -6,7 +6,7 @@ export function main() {
describe("Form Directives", () => { describe("Form Directives", () => {
describe("Control", () => { describe("Control", () => {
it("should throw when the group is not found and the control is not set", () => { it("should throw when the group is not found and the control is not set", () => {
var c = new ControlDirective(null, null); var c = new ControlDirective(null);
expect(() => { expect(() => {
c.controlOrName = 'login'; c.controlOrName = 'login';
}).toThrowError(new RegExp('No control group found for "login"')); }).toThrowError(new RegExp('No control group found for "login"'));
@ -16,7 +16,7 @@ export function main() {
var emptyGroup = new ControlGroupDirective(null); var emptyGroup = new ControlGroupDirective(null);
emptyGroup.controlOrName = new ControlGroup({}); emptyGroup.controlOrName = new ControlGroup({});
var c = new ControlDirective(emptyGroup, null); var c = new ControlDirective(emptyGroup);
expect(() => { expect(() => {
c.controlOrName = 'login'; c.controlOrName = 'login';
}).toThrowError(new RegExp('Cannot find control "login"')); }).toThrowError(new RegExp('Cannot find control "login"'));

View File

@ -21,8 +21,7 @@ import {View} from 'angular2/src/core/annotations_impl/view';
import {TestBed} from 'angular2/src/test_lib/test_bed'; import {TestBed} from 'angular2/src/test_lib/test_bed';
import {ControlGroupDirective, ControlDirective, Control, ControlGroup, RequiredValidatorDirective, CheckboxControlValueAccessor, import {ControlGroupDirective, ControlDirective, Control, ControlGroup, RequiredValidatorDirective, CheckboxControlValueAccessor, DefaultValueAccessor, SelectControlValueAccessor, Validators} from 'angular2/forms';
DefaultValueAccessor, Validators} from 'angular2/forms';
export function main() { export function main() {
describe("integration tests", () => { describe("integration tests", () => {
@ -266,6 +265,19 @@ export function main() {
async.done(); async.done();
}); });
})); }));
it("should throw when cannot find a value accessor", inject([TestBed, AsyncTestCompleter], (tb, async) => {
var ctx = new MyComp(new ControlGroup({"name": new Control("aa")}));
var t = `<div [control-group]="form">
<div control="name">
</div>`;
tb.createView(MyComp, {context: ctx, html: t}).then((view) => {
expect(() => view.detectChanges()).toThrowError(new RegExp("Cannot find value accessor"))
async.done();
});
}));
}); });
describe("validations", () => { describe("validations", () => {
@ -377,7 +389,9 @@ export function main() {
WrappedValue, WrappedValue,
RequiredValidatorDirective, RequiredValidatorDirective,
DefaultValueAccessor, DefaultValueAccessor,
CheckboxControlValueAccessor]}) CheckboxControlValueAccessor,
SelectControlValueAccessor
]})
class MyComp { class MyComp {
form:any; form:any;
name:string; name:string;