diff --git a/modules/angular2/src/forms/directives.ts b/modules/angular2/src/forms/directives.ts index 6b8bf05c4a..c9ecd0560a 100644 --- a/modules/angular2/src/forms/directives.ts +++ b/modules/angular2/src/forms/directives.ts @@ -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 - * ``` - * - * ``` - * - * @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 + * ``` + * + * ``` + * + * @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 + * ``` + * + * ``` + * + * @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 = CONST_EXPR( - [ControlGroupDirective, ControlDirective, CheckboxControlValueAccessor, DefaultValueAccessor]); +export const formDirectives: List = CONST_EXPR([ + ControlGroupDirective, + ControlDirective, + CheckboxControlValueAccessor, + DefaultValueAccessor, + SelectControlValueAccessor +]); diff --git a/modules/angular2/test/forms/directives_spec.js b/modules/angular2/test/forms/directives_spec.js index 703652fa82..5e1dcf723b 100644 --- a/modules/angular2/test/forms/directives_spec.js +++ b/modules/angular2/test/forms/directives_spec.js @@ -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"')); diff --git a/modules/angular2/test/forms/integration_spec.js b/modules/angular2/test/forms/integration_spec.js index fa3df617e8..bfb7176b47 100644 --- a/modules/angular2/test/forms/integration_spec.js +++ b/modules/angular2/test/forms/integration_spec.js @@ -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 = `
+
+
`; + + 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;