fix(forms): changed forms to create only one value accessor instead of always creating DefaultValueAccessor
This commit is contained in:
parent
2ff3873881
commit
30c3e5a84e
|
@ -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
|
||||||
|
]);
|
||||||
|
|
|
@ -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"'));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue