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;
}
/**
* 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.
*
@ -171,11 +142,9 @@ export class ControlDirective {
validator: Function;
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective,
valueAccessor: DefaultValueAccessor) {
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective) {
this._groupDirective = groupDirective;
this._controlOrName = null;
this.valueAccessor = valueAccessor;
this.validator = Validators.nullValidator;
}
@ -189,6 +158,10 @@ export class ControlDirective {
var c = this._control();
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._setUpUpdateControlValue();
}
@ -202,6 +175,75 @@ export class ControlDirective {
_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.
*
@ -219,17 +261,12 @@ export class ControlDirective {
hostProperties: {'checked': 'checked'}
})
export class CheckboxControlValueAccessor {
_elementRef: ElementRef;
_renderer: Renderer;
checked: boolean;
onChange: Function;
constructor(cd: ControlDirective, elementRef: ElementRef, renderer: Renderer) {
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
this.onChange = (_) => {};
this._elementRef = elementRef;
this._renderer = renderer;
cd.valueAccessor = this; // ControlDirective should inject CheckboxControlDirective
cd.valueAccessor = this;
}
writeValue(value) {
@ -246,5 +283,10 @@ export class CheckboxControlValueAccessor {
*
* @exportedAs angular2/forms
*/
export const formDirectives: List<Type> = CONST_EXPR(
[ControlGroupDirective, ControlDirective, CheckboxControlValueAccessor, DefaultValueAccessor]);
export const formDirectives: List<Type> = CONST_EXPR([
ControlGroupDirective,
ControlDirective,
CheckboxControlValueAccessor,
DefaultValueAccessor,
SelectControlValueAccessor
]);

View File

@ -6,7 +6,7 @@ export function main() {
describe("Form Directives", () => {
describe("Control", () => {
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(() => {
c.controlOrName = 'login';
}).toThrowError(new RegExp('No control group found for "login"'));
@ -16,7 +16,7 @@ export function main() {
var emptyGroup = new ControlGroupDirective(null);
emptyGroup.controlOrName = new ControlGroup({});
var c = new ControlDirective(emptyGroup, null);
var c = new ControlDirective(emptyGroup);
expect(() => {
c.controlOrName = '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 {ControlGroupDirective, ControlDirective, Control, ControlGroup, RequiredValidatorDirective, CheckboxControlValueAccessor,
DefaultValueAccessor, Validators} from 'angular2/forms';
import {ControlGroupDirective, ControlDirective, Control, ControlGroup, RequiredValidatorDirective, CheckboxControlValueAccessor, DefaultValueAccessor, SelectControlValueAccessor, Validators} from 'angular2/forms';
export function main() {
describe("integration tests", () => {
@ -266,6 +265,19 @@ export function main() {
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", () => {
@ -377,7 +389,9 @@ export function main() {
WrappedValue,
RequiredValidatorDirective,
DefaultValueAccessor,
CheckboxControlValueAccessor]})
CheckboxControlValueAccessor,
SelectControlValueAccessor
]})
class MyComp {
form:any;
name:string;