feat(forms): make NgControl -> NgValueAccessor dependency unidirectional
Closes #4421
This commit is contained in:
parent
7b2d8fce07
commit
00a4b2e28f
|
@ -26,6 +26,7 @@ export {NgFormModel} from './forms/directives/ng_form_model';
|
||||||
export {NgForm} from './forms/directives/ng_form';
|
export {NgForm} from './forms/directives/ng_form';
|
||||||
export {ControlValueAccessor} from './forms/directives/control_value_accessor';
|
export {ControlValueAccessor} from './forms/directives/control_value_accessor';
|
||||||
export {DefaultValueAccessor} from './forms/directives/default_value_accessor';
|
export {DefaultValueAccessor} from './forms/directives/default_value_accessor';
|
||||||
|
export {NgControlStatus} from './forms/directives/ng_control_status';
|
||||||
export {CheckboxControlValueAccessor} from './forms/directives/checkbox_value_accessor';
|
export {CheckboxControlValueAccessor} from './forms/directives/checkbox_value_accessor';
|
||||||
export {
|
export {
|
||||||
NgSelectOption,
|
NgSelectOption,
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import {Directive} from 'angular2/src/core/metadata';
|
import {Directive} from 'angular2/src/core/metadata';
|
||||||
import {Renderer} from 'angular2/src/core/render';
|
import {Renderer} from 'angular2/src/core/render';
|
||||||
import {ElementRef} from 'angular2/src/core/compiler';
|
import {ElementRef} from 'angular2/src/core/compiler';
|
||||||
import {Self} from 'angular2/src/core/di';
|
import {Self, forwardRef, Binding} from 'angular2/src/core/di';
|
||||||
|
|
||||||
import {NgControl} from './ng_control';
|
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
import {setProperty} from './shared';
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
|
const CHECKBOX_VALUE_ACCESSOR = CONST_EXPR(new Binding(
|
||||||
|
NG_VALUE_ACCESSOR, {toAlias: forwardRef(() => CheckboxControlValueAccessor), multi: true}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
|
@ -19,21 +21,16 @@ import {setProperty} from './shared';
|
||||||
@Directive({
|
@Directive({
|
||||||
selector:
|
selector:
|
||||||
'input[type=checkbox][ng-control],input[type=checkbox][ng-form-control],input[type=checkbox][ng-model]',
|
'input[type=checkbox][ng-control],input[type=checkbox][ng-form-control],input[type=checkbox][ng-model]',
|
||||||
host: {
|
host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
|
||||||
'(change)': 'onChange($event.target.checked)',
|
bindings: [CHECKBOX_VALUE_ACCESSOR]
|
||||||
'(blur)': 'onTouched()'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||||
onChange = (_) => {};
|
onChange = (_) => {};
|
||||||
onTouched = () => {};
|
onTouched = () => {};
|
||||||
|
|
||||||
constructor(@Self() cd: NgControl, private _renderer: Renderer, private _elementRef: ElementRef) {
|
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||||
cd.valueAccessor = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeValue(value: any): void { setProperty(this._renderer, this._elementRef, "checked", value); }
|
writeValue(value: any): void { setProperty(this._renderer, this._elementRef, "checked", value); }
|
||||||
|
|
||||||
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
|
import {OpaqueToken} from 'angular2/src/core/di';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bridge between a control and a native element.
|
* A bridge between a control and a native element.
|
||||||
*
|
*
|
||||||
|
@ -7,4 +10,6 @@ export interface ControlValueAccessor {
|
||||||
writeValue(obj: any): void;
|
writeValue(obj: any): void;
|
||||||
registerOnChange(fn: any): void;
|
registerOnChange(fn: any): void;
|
||||||
registerOnTouched(fn: any): void;
|
registerOnTouched(fn: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NG_VALUE_ACCESSOR: OpaqueToken = CONST_EXPR(new OpaqueToken("NgValueAccessor"));
|
|
@ -1,12 +1,14 @@
|
||||||
import {Directive} from 'angular2/src/core/metadata';
|
import {Directive} from 'angular2/src/core/metadata';
|
||||||
import {ElementRef} from 'angular2/src/core/compiler';
|
import {ElementRef} from 'angular2/src/core/compiler';
|
||||||
import {Renderer} from 'angular2/src/core/render';
|
import {Renderer} from 'angular2/src/core/render';
|
||||||
import {Self} from 'angular2/src/core/di';
|
import {Self, forwardRef, Binding} from 'angular2/src/core/di';
|
||||||
import {NgControl} from './ng_control';
|
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {isBlank, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
import {setProperty} from './shared';
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
|
const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(
|
||||||
|
new Binding(NG_VALUE_ACCESSOR, {toAlias: forwardRef(() => DefaultValueAccessor), multi: true}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default accessor for writing a value and listening to changes that is used by the
|
* The default accessor for writing a value and listening to changes that is used by the
|
||||||
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
|
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
|
||||||
|
@ -17,21 +19,19 @@ import {setProperty} from './shared';
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector:
|
selector: '[ng-control],[ng-model],[ng-form-control]',
|
||||||
'input:not([type=checkbox])[ng-control],textarea[ng-control],input:not([type=checkbox])[ng-form-control],textarea[ng-form-control],input:not([type=checkbox])[ng-model],textarea[ng-model]',
|
|
||||||
host: {
|
host: {
|
||||||
'(change)': 'onChange($event.target.value)',
|
'(change)': 'onChange($event.target.value)',
|
||||||
'(input)': 'onChange($event.target.value)',
|
'(input)': 'onChange($event.target.value)',
|
||||||
'(blur)': 'onTouched()'
|
'(blur)': 'onTouched()'
|
||||||
}
|
},
|
||||||
|
bindings: [DEFAULT_VALUE_ACCESSOR]
|
||||||
})
|
})
|
||||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||||
onChange = (_) => {};
|
onChange = (_) => {};
|
||||||
onTouched = () => {};
|
onTouched = () => {};
|
||||||
|
|
||||||
constructor(@Self() cd: NgControl, private _renderer: Renderer, private _elementRef: ElementRef) {
|
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||||
cd.valueAccessor = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeValue(value: any): void {
|
writeValue(value: any): void {
|
||||||
var normalizedValue = isBlank(value) ? '' : value;
|
var normalizedValue = isBlank(value) ? '' : value;
|
||||||
|
|
|
@ -8,7 +8,8 @@ import {forwardRef, Host, SkipSelf, Binding, Inject, Optional} from 'angular2/sr
|
||||||
|
|
||||||
import {ControlContainer} from './control_container';
|
import {ControlContainer} from './control_container';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {controlPath, isPropertyUpdated} from './shared';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
|
import {controlPath, isPropertyUpdated, selectValueAccessor} from './shared';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {Validators, NG_VALIDATORS} from '../validators';
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
|
|
||||||
|
@ -88,10 +89,12 @@ export class NgControlName extends NgControl implements OnChanges,
|
||||||
_added = false;
|
_added = false;
|
||||||
|
|
||||||
constructor(@Host() @SkipSelf() parent: ControlContainer,
|
constructor(@Host() @SkipSelf() parent: ControlContainer,
|
||||||
@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
|
@Optional() @Inject(NG_VALIDATORS) validators: Function[],
|
||||||
|
@Optional() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
|
||||||
super();
|
super();
|
||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
this.validators = validators;
|
this.validators = validators;
|
||||||
|
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChanges(changes: StringMap<string, SimpleChange>) {
|
onChanges(changes: StringMap<string, SimpleChange>) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {NgControl} from './ng_control';
|
||||||
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector:'[ng-control],[ng-model],[ng-form-control]',
|
selector: '[ng-control],[ng-model],[ng-form-control]',
|
||||||
host: {
|
host: {
|
||||||
'[class.ng-untouched]': 'ngClassUntouched',
|
'[class.ng-untouched]': 'ngClassUntouched',
|
||||||
'[class.ng-touched]': 'ngClassTouched',
|
'[class.ng-touched]': 'ngClassTouched',
|
||||||
|
|
|
@ -7,7 +7,8 @@ import {forwardRef, Binding, Inject, Optional} from 'angular2/src/core/di';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {Validators, NG_VALIDATORS} from '../validators';
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
import {setUpControl, isPropertyUpdated} from './shared';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
|
import {setUpControl, isPropertyUpdated, selectValueAccessor} from './shared';
|
||||||
|
|
||||||
const formControlBinding =
|
const formControlBinding =
|
||||||
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgFormControl)}));
|
CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgFormControl)}));
|
||||||
|
@ -76,9 +77,11 @@ export class NgFormControl extends NgControl implements OnChanges {
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
validators: Function[];
|
validators: Function[];
|
||||||
|
|
||||||
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
|
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[],
|
||||||
|
@Optional() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
|
||||||
super();
|
super();
|
||||||
this.validators = validators;
|
this.validators = validators;
|
||||||
|
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChanges(changes: StringMap<string, SimpleChange>): void {
|
onChanges(changes: StringMap<string, SimpleChange>): void {
|
||||||
|
|
|
@ -4,10 +4,11 @@ import {OnChanges} from 'angular2/lifecycle_hooks';
|
||||||
import {SimpleChange} from 'angular2/src/core/change_detection';
|
import {SimpleChange} from 'angular2/src/core/change_detection';
|
||||||
import {Query, Directive} from 'angular2/src/core/metadata';
|
import {Query, Directive} from 'angular2/src/core/metadata';
|
||||||
import {forwardRef, Binding, Inject, Optional} from 'angular2/src/core/di';
|
import {forwardRef, Binding, Inject, Optional} from 'angular2/src/core/di';
|
||||||
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {Validators, NG_VALIDATORS} from '../validators';
|
import {Validators, NG_VALIDATORS} from '../validators';
|
||||||
import {setUpControl, isPropertyUpdated} from './shared';
|
import {setUpControl, isPropertyUpdated, selectValueAccessor} from './shared';
|
||||||
|
|
||||||
const formControlBinding = CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgModel)}));
|
const formControlBinding = CONST_EXPR(new Binding(NgControl, {toAlias: forwardRef(() => NgModel)}));
|
||||||
|
|
||||||
|
@ -47,9 +48,11 @@ export class NgModel extends NgControl implements OnChanges {
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
validators: Function[];
|
validators: Function[];
|
||||||
|
|
||||||
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[]) {
|
constructor(@Optional() @Inject(NG_VALIDATORS) validators: Function[],
|
||||||
|
@Optional() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
|
||||||
super();
|
super();
|
||||||
this.validators = validators;
|
this.validators = validators;
|
||||||
|
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChanges(changes: StringMap<string, SimpleChange>) {
|
onChanges(changes: StringMap<string, SimpleChange>) {
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import {Self} from 'angular2/src/core/di';
|
import {Self, forwardRef, Binding} from 'angular2/src/core/di';
|
||||||
import {Renderer} from 'angular2/src/core/render';
|
import {Renderer} from 'angular2/src/core/render';
|
||||||
import {ElementRef, QueryList} from 'angular2/src/core/compiler';
|
import {ElementRef, QueryList} from 'angular2/src/core/compiler';
|
||||||
import {Query, Directive} from 'angular2/src/core/metadata';
|
import {Query, Directive} from 'angular2/src/core/metadata';
|
||||||
|
|
||||||
import {NgControl} from './ng_control';
|
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||||
|
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {setProperty} from './shared';
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
|
const SELECT_VALUE_ACCESSOR = CONST_EXPR(new Binding(
|
||||||
|
NG_VALUE_ACCESSOR, {toAlias: forwardRef(() => SelectControlValueAccessor), multi: true}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||||
*
|
*
|
||||||
|
@ -33,16 +35,16 @@ export class NgSelectOption {
|
||||||
'(change)': 'onChange($event.target.value)',
|
'(change)': 'onChange($event.target.value)',
|
||||||
'(input)': 'onChange($event.target.value)',
|
'(input)': 'onChange($event.target.value)',
|
||||||
'(blur)': 'onTouched()'
|
'(blur)': 'onTouched()'
|
||||||
}
|
},
|
||||||
|
bindings: [SELECT_VALUE_ACCESSOR]
|
||||||
})
|
})
|
||||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||||
value: string;
|
value: string;
|
||||||
onChange = (_) => {};
|
onChange = (_) => {};
|
||||||
onTouched = () => {};
|
onTouched = () => {};
|
||||||
|
|
||||||
constructor(@Self() cd: NgControl, private _renderer: Renderer, private _elementRef: ElementRef,
|
constructor(private _renderer: Renderer, private _elementRef: ElementRef,
|
||||||
@Query(NgSelectOption, {descendants: true}) query: QueryList<NgSelectOption>) {
|
@Query(NgSelectOption, {descendants: true}) query: QueryList<NgSelectOption>) {
|
||||||
cd.valueAccessor = this;
|
|
||||||
this._updateValueWhenListOfOptionsChanges(query);
|
this._updateValueWhenListOfOptionsChanges(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {isBlank, looseIdentical} from 'angular2/src/core/facade/lang';
|
import {isBlank, isPresent, looseIdentical} from 'angular2/src/core/facade/lang';
|
||||||
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
||||||
|
|
||||||
import {ControlContainer} from './control_container';
|
import {ControlContainer} from './control_container';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {Validators} from '../validators';
|
import {Validators} from '../validators';
|
||||||
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {ElementRef, QueryList} from 'angular2/src/core/compiler';
|
import {ElementRef, QueryList} from 'angular2/src/core/compiler';
|
||||||
import {Renderer} from 'angular2/src/core/render';
|
import {Renderer} from 'angular2/src/core/render';
|
||||||
|
import {DefaultValueAccessor} from './default_value_accessor';
|
||||||
|
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
||||||
|
import {SelectControlValueAccessor} from './select_control_value_accessor';
|
||||||
|
|
||||||
|
|
||||||
export function controlPath(name: string, parent: ControlContainer): string[] {
|
export function controlPath(name: string, parent: ControlContainer): string[] {
|
||||||
|
@ -54,3 +58,37 @@ export function isPropertyUpdated(changes: StringMap<string, any>, viewModel: an
|
||||||
if (change.isFirstChange()) return true;
|
if (change.isFirstChange()) return true;
|
||||||
return !looseIdentical(viewModel, change.currentValue);
|
return !looseIdentical(viewModel, change.currentValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented
|
||||||
|
export function selectValueAccessor(dir: NgControl, valueAccessors: ControlValueAccessor[]):
|
||||||
|
ControlValueAccessor {
|
||||||
|
if (isBlank(valueAccessors)) return null;
|
||||||
|
|
||||||
|
var defaultAccessor;
|
||||||
|
var builtinAccessor;
|
||||||
|
var customAccessor;
|
||||||
|
|
||||||
|
valueAccessors.forEach(v => {
|
||||||
|
if (v instanceof DefaultValueAccessor) {
|
||||||
|
defaultAccessor = v;
|
||||||
|
|
||||||
|
} else if (v instanceof CheckboxControlValueAccessor ||
|
||||||
|
v instanceof SelectControlValueAccessor) {
|
||||||
|
if (isPresent(builtinAccessor))
|
||||||
|
_throwError(dir, "More than one built-in value accessor matches");
|
||||||
|
builtinAccessor = v;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (isPresent(customAccessor))
|
||||||
|
_throwError(dir, "More than one custom value accessor matches");
|
||||||
|
customAccessor = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isPresent(customAccessor)) return customAccessor;
|
||||||
|
if (isPresent(builtinAccessor)) return builtinAccessor;
|
||||||
|
if (isPresent(defaultAccessor)) return defaultAccessor;
|
||||||
|
|
||||||
|
_throwError(dir, "No valid value accessor for");
|
||||||
|
return null;
|
||||||
|
}
|
|
@ -14,6 +14,9 @@ import {
|
||||||
inject
|
inject
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {SpyNgControl, SpyValueAccessor} from '../spies';
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ControlGroup,
|
ControlGroup,
|
||||||
Control,
|
Control,
|
||||||
|
@ -25,9 +28,18 @@ import {
|
||||||
NgForm,
|
NgForm,
|
||||||
NgModel,
|
NgModel,
|
||||||
NgFormControl,
|
NgFormControl,
|
||||||
DefaultValidators
|
DefaultValidators,
|
||||||
|
NgControl,
|
||||||
|
DefaultValueAccessor,
|
||||||
|
CheckboxControlValueAccessor,
|
||||||
|
SelectControlValueAccessor,
|
||||||
|
QueryList
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
|
|
||||||
|
|
||||||
|
import {selectValueAccessor} from 'angular2/src/core/forms/directives/shared';
|
||||||
|
|
||||||
|
|
||||||
class DummyControlValueAccessor implements ControlValueAccessor {
|
class DummyControlValueAccessor implements ControlValueAccessor {
|
||||||
writtenValue;
|
writtenValue;
|
||||||
|
|
||||||
|
@ -39,6 +51,54 @@ class DummyControlValueAccessor implements ControlValueAccessor {
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("Form Directives", () => {
|
describe("Form Directives", () => {
|
||||||
|
var defaultAccessor;
|
||||||
|
|
||||||
|
beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null, null); });
|
||||||
|
|
||||||
|
describe("shared", () => {
|
||||||
|
describe("selectValueAccessor", () => {
|
||||||
|
var dir: NgControl;
|
||||||
|
|
||||||
|
beforeEach(() => { dir = <any>new SpyNgControl(); });
|
||||||
|
|
||||||
|
it("should throw when given an empty array",
|
||||||
|
() => { expect(() => selectValueAccessor(dir, [])).toThrowError(); });
|
||||||
|
|
||||||
|
it("should return the default value accessor when no other provided",
|
||||||
|
() => { expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); });
|
||||||
|
|
||||||
|
it("should return checkbox accessor when provided", () => {
|
||||||
|
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||||
|
expect(selectValueAccessor(dir, [defaultAccessor, checkboxAccessor]))
|
||||||
|
.toEqual(checkboxAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return select accessor when provided", () => {
|
||||||
|
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
|
||||||
|
expect(selectValueAccessor(dir, [defaultAccessor, selectAccessor]))
|
||||||
|
.toEqual(selectAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw when more than one build-in accessor is provided", () => {
|
||||||
|
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||||
|
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
|
||||||
|
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return custom accessor when provided", () => {
|
||||||
|
var customAccessor = new SpyValueAccessor();
|
||||||
|
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||||
|
expect(selectValueAccessor(dir, [defaultAccessor, customAccessor, checkboxAccessor]))
|
||||||
|
.toEqual(customAccessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw when more than one custom accessor is provided", () => {
|
||||||
|
var customAccessor = new SpyValueAccessor();
|
||||||
|
expect(() => selectValueAccessor(dir, [customAccessor, customAccessor])).toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("NgFormModel", () => {
|
describe("NgFormModel", () => {
|
||||||
var form;
|
var form;
|
||||||
var formModel;
|
var formModel;
|
||||||
|
@ -49,7 +109,7 @@ export function main() {
|
||||||
formModel = new ControlGroup({"login": new Control(null)});
|
formModel = new ControlGroup({"login": new Control(null)});
|
||||||
form.form = formModel;
|
form.form = formModel;
|
||||||
|
|
||||||
loginControlDir = new NgControlName(form, []);
|
loginControlDir = new NgControlName(form, [], [defaultAccessor]);
|
||||||
loginControlDir.name = "login";
|
loginControlDir.name = "login";
|
||||||
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
@ -67,7 +127,7 @@ export function main() {
|
||||||
|
|
||||||
describe("addControl", () => {
|
describe("addControl", () => {
|
||||||
it("should throw when no control found", () => {
|
it("should throw when no control found", () => {
|
||||||
var dir = new NgControlName(form, null);
|
var dir = new NgControlName(form, null, [defaultAccessor]);
|
||||||
dir.name = "invalidName";
|
dir.name = "invalidName";
|
||||||
|
|
||||||
expect(() => form.addControl(dir))
|
expect(() => form.addControl(dir))
|
||||||
|
@ -75,7 +135,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw when no value accessor", () => {
|
it("should throw when no value accessor", () => {
|
||||||
var dir = new NgControlName(form, null);
|
var dir = new NgControlName(form, null, null);
|
||||||
dir.name = "login";
|
dir.name = "login";
|
||||||
|
|
||||||
expect(() => form.addControl(dir))
|
expect(() => form.addControl(dir))
|
||||||
|
@ -141,7 +201,7 @@ export function main() {
|
||||||
personControlGroupDir = new NgControlGroup(form);
|
personControlGroupDir = new NgControlGroup(form);
|
||||||
personControlGroupDir.name = "person";
|
personControlGroupDir.name = "person";
|
||||||
|
|
||||||
loginControlDir = new NgControlName(personControlGroupDir, null);
|
loginControlDir = new NgControlName(personControlGroupDir, null, [defaultAccessor]);
|
||||||
loginControlDir.name = "login";
|
loginControlDir.name = "login";
|
||||||
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
@ -218,7 +278,7 @@ export function main() {
|
||||||
var control;
|
var control;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
controlDir = new NgFormControl([]);
|
controlDir = new NgFormControl([], [defaultAccessor]);
|
||||||
controlDir.valueAccessor = new DummyControlValueAccessor();
|
controlDir.valueAccessor = new DummyControlValueAccessor();
|
||||||
|
|
||||||
control = new Control(null);
|
control = new Control(null);
|
||||||
|
@ -252,7 +312,7 @@ export function main() {
|
||||||
var ngModel;
|
var ngModel;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ngModel = new NgModel([]);
|
ngModel = new NgModel([], [defaultAccessor]);
|
||||||
ngModel.valueAccessor = new DummyControlValueAccessor();
|
ngModel.valueAccessor = new DummyControlValueAccessor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -289,7 +349,7 @@ export function main() {
|
||||||
|
|
||||||
var parent = new NgFormModel();
|
var parent = new NgFormModel();
|
||||||
parent.form = new ControlGroup({"name": formModel});
|
parent.form = new ControlGroup({"name": formModel});
|
||||||
controlNameDir = new NgControlName(parent, []);
|
controlNameDir = new NgControlName(parent, [], [defaultAccessor]);
|
||||||
controlNameDir.name = "name";
|
controlNameDir.name = "name";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import {By} from 'angular2/src/core/debug';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("integration tests", () => {
|
describe("integration tests", () => {
|
||||||
|
|
||||||
it("should initialize DOM elements with the given form object",
|
it("should initialize DOM elements with the given form object",
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
var t = `<div [ng-form-model]="form">
|
var t = `<div [ng-form-model]="form">
|
||||||
|
|
|
@ -120,3 +120,13 @@ class SpyXHR extends SpyObject implements XHR {
|
||||||
class SpyRenderEventDispatcher extends SpyObject implements RenderEventDispatcher {
|
class SpyRenderEventDispatcher extends SpyObject implements RenderEventDispatcher {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyNgControl extends SpyObject implements NgControl {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyValueAccessor extends SpyObject implements ControlValueAccessor {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
|
@ -105,3 +105,7 @@ export class SpyRenderEventDispatcher extends SpyObject {
|
||||||
this.spy('dispatchRenderEvent');
|
this.spy('dispatchRenderEvent');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SpyNgControl extends SpyObject {}
|
||||||
|
|
||||||
|
export class SpyValueAccessor extends SpyObject {}
|
||||||
|
|
|
@ -142,12 +142,6 @@ var NG_API = [
|
||||||
'ChangeDetectorRef.markForCheck()',
|
'ChangeDetectorRef.markForCheck()',
|
||||||
'ChangeDetectorRef.reattach()',
|
'ChangeDetectorRef.reattach()',
|
||||||
'CheckboxControlValueAccessor',
|
'CheckboxControlValueAccessor',
|
||||||
'CheckboxControlValueAccessor.ngClassDirty',
|
|
||||||
'CheckboxControlValueAccessor.ngClassInvalid',
|
|
||||||
'CheckboxControlValueAccessor.ngClassPristine',
|
|
||||||
'CheckboxControlValueAccessor.ngClassTouched',
|
|
||||||
'CheckboxControlValueAccessor.ngClassUntouched',
|
|
||||||
'CheckboxControlValueAccessor.ngClassValid',
|
|
||||||
'CheckboxControlValueAccessor.onChange',
|
'CheckboxControlValueAccessor.onChange',
|
||||||
'CheckboxControlValueAccessor.onChange=',
|
'CheckboxControlValueAccessor.onChange=',
|
||||||
'CheckboxControlValueAccessor.onTouched',
|
'CheckboxControlValueAccessor.onTouched',
|
||||||
|
@ -355,12 +349,6 @@ var NG_API = [
|
||||||
'DecimalPipe.transform()',
|
'DecimalPipe.transform()',
|
||||||
'DefaultValidators',
|
'DefaultValidators',
|
||||||
'DefaultValueAccessor',
|
'DefaultValueAccessor',
|
||||||
'DefaultValueAccessor.ngClassDirty',
|
|
||||||
'DefaultValueAccessor.ngClassInvalid',
|
|
||||||
'DefaultValueAccessor.ngClassPristine',
|
|
||||||
'DefaultValueAccessor.ngClassTouched',
|
|
||||||
'DefaultValueAccessor.ngClassUntouched',
|
|
||||||
'DefaultValueAccessor.ngClassValid',
|
|
||||||
'DefaultValueAccessor.onChange',
|
'DefaultValueAccessor.onChange',
|
||||||
'DefaultValueAccessor.onChange=',
|
'DefaultValueAccessor.onChange=',
|
||||||
'DefaultValueAccessor.onTouched',
|
'DefaultValueAccessor.onTouched',
|
||||||
|
@ -587,6 +575,13 @@ var NG_API = [
|
||||||
'NgControlGroup.untouched',
|
'NgControlGroup.untouched',
|
||||||
'NgControlGroup.valid',
|
'NgControlGroup.valid',
|
||||||
'NgControlGroup.value',
|
'NgControlGroup.value',
|
||||||
|
'NgControlStatus',
|
||||||
|
'NgControlStatus.ngClassDirty',
|
||||||
|
'NgControlStatus.ngClassInvalid',
|
||||||
|
'NgControlStatus.ngClassPristine',
|
||||||
|
'NgControlStatus.ngClassTouched',
|
||||||
|
'NgControlStatus.ngClassUntouched',
|
||||||
|
'NgControlStatus.ngClassValid',
|
||||||
'NgControlName',
|
'NgControlName',
|
||||||
'NgControlName.control',
|
'NgControlName.control',
|
||||||
'NgControlName.dirty',
|
'NgControlName.dirty',
|
||||||
|
@ -936,12 +931,6 @@ var NG_API = [
|
||||||
'Scope#view()',
|
'Scope#view()',
|
||||||
'Scope', // TODO(misko): rename?
|
'Scope', // TODO(misko): rename?
|
||||||
'SelectControlValueAccessor',
|
'SelectControlValueAccessor',
|
||||||
'SelectControlValueAccessor.ngClassDirty',
|
|
||||||
'SelectControlValueAccessor.ngClassInvalid',
|
|
||||||
'SelectControlValueAccessor.ngClassPristine',
|
|
||||||
'SelectControlValueAccessor.ngClassTouched',
|
|
||||||
'SelectControlValueAccessor.ngClassUntouched',
|
|
||||||
'SelectControlValueAccessor.ngClassValid',
|
|
||||||
'SelectControlValueAccessor.onChange',
|
'SelectControlValueAccessor.onChange',
|
||||||
'SelectControlValueAccessor.onChange=',
|
'SelectControlValueAccessor.onChange=',
|
||||||
'SelectControlValueAccessor.onTouched',
|
'SelectControlValueAccessor.onTouched',
|
||||||
|
|
Loading…
Reference in New Issue