style(forms): reformat of the forms
package after clang update (#36466)
PR Close #36466
This commit is contained in:

committed by
Kara Erickson

parent
aeb6d0d0f7
commit
f48a065db0
@ -19,22 +19,30 @@ class DummyControlValueAccessor implements ControlValueAccessor {
|
||||
registerOnChange(fn: any) {}
|
||||
registerOnTouched(fn: any) {}
|
||||
|
||||
writeValue(obj: any): void { this.writtenValue = obj; }
|
||||
writeValue(obj: any): void {
|
||||
this.writtenValue = obj;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomValidatorDirective implements Validator {
|
||||
validate(c: FormControl): ValidationErrors { return {'custom': true}; }
|
||||
validate(c: FormControl): ValidationErrors {
|
||||
return {'custom': true};
|
||||
}
|
||||
}
|
||||
|
||||
function asyncValidator(expected: any, timeout = 0) {
|
||||
return (c: AbstractControl): any => {
|
||||
let resolve: (result: any) => void = undefined !;
|
||||
const promise = new Promise(res => { resolve = res; });
|
||||
let resolve: (result: any) => void = undefined!;
|
||||
const promise = new Promise(res => {
|
||||
resolve = res;
|
||||
});
|
||||
const res = c.value != expected ? {'async': true} : null;
|
||||
if (timeout == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => { resolve(res); }, timeout);
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, timeout);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
@ -44,16 +52,21 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
describe('Form Directives', () => {
|
||||
let defaultAccessor: DefaultValueAccessor;
|
||||
|
||||
beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null !, null !, null !); });
|
||||
beforeEach(() => {
|
||||
defaultAccessor = new DefaultValueAccessor(null!, null!, null!);
|
||||
});
|
||||
|
||||
describe('shared', () => {
|
||||
describe('selectValueAccessor', () => {
|
||||
let dir: NgControl;
|
||||
|
||||
beforeEach(() => { dir = <any>new SpyNgControl(); });
|
||||
beforeEach(() => {
|
||||
dir = <any>new SpyNgControl();
|
||||
});
|
||||
|
||||
it('should throw when given an empty array',
|
||||
() => { expect(() => selectValueAccessor(dir, [])).toThrowError(); });
|
||||
it('should throw when given an empty array', () => {
|
||||
expect(() => selectValueAccessor(dir, [])).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw when accessor is not provided as array', () => {
|
||||
expect(() => selectValueAccessor(dir, {} as any[]))
|
||||
@ -61,49 +74,51 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
`Value accessor was not provided as an array for form control with unspecified name attribute`);
|
||||
});
|
||||
|
||||
it('should return the default value accessor when no other provided',
|
||||
() => { expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); });
|
||||
it('should return the default value accessor when no other provided', () => {
|
||||
expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor);
|
||||
});
|
||||
|
||||
it('should return checkbox accessor when provided', () => {
|
||||
const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !);
|
||||
const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!);
|
||||
expect(selectValueAccessor(dir, [
|
||||
defaultAccessor, checkboxAccessor
|
||||
])).toEqual(checkboxAccessor);
|
||||
});
|
||||
|
||||
it('should return select accessor when provided', () => {
|
||||
const selectAccessor = new SelectControlValueAccessor(null !, null !);
|
||||
const selectAccessor = new SelectControlValueAccessor(null!, null!);
|
||||
expect(selectValueAccessor(dir, [
|
||||
defaultAccessor, selectAccessor
|
||||
])).toEqual(selectAccessor);
|
||||
});
|
||||
|
||||
it('should return select multiple accessor when provided', () => {
|
||||
const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null !, null !);
|
||||
const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null!, null!);
|
||||
expect(selectValueAccessor(dir, [
|
||||
defaultAccessor, selectMultipleAccessor
|
||||
])).toEqual(selectMultipleAccessor);
|
||||
});
|
||||
|
||||
it('should throw when more than one build-in accessor is provided', () => {
|
||||
const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !);
|
||||
const selectAccessor = new SelectControlValueAccessor(null !, null !);
|
||||
const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!);
|
||||
const selectAccessor = new SelectControlValueAccessor(null!, null!);
|
||||
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
|
||||
});
|
||||
|
||||
it('should return custom accessor when provided', () => {
|
||||
const customAccessor: ControlValueAccessor = new SpyValueAccessor() as any;
|
||||
const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !);
|
||||
expect(selectValueAccessor(dir, <any>[defaultAccessor, customAccessor, checkboxAccessor]))
|
||||
.toEqual(customAccessor);
|
||||
const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!);
|
||||
expect(selectValueAccessor(dir, <any>[
|
||||
defaultAccessor, customAccessor, checkboxAccessor
|
||||
])).toEqual(customAccessor);
|
||||
});
|
||||
|
||||
it('should return custom accessor when provided with select multiple', () => {
|
||||
const customAccessor: ControlValueAccessor = new SpyValueAccessor() as any;
|
||||
const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null !, null !);
|
||||
expect(selectValueAccessor(
|
||||
dir, <any>[defaultAccessor, customAccessor, selectMultipleAccessor]))
|
||||
.toEqual(customAccessor);
|
||||
const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null!, null!);
|
||||
expect(selectValueAccessor(dir, <any>[
|
||||
defaultAccessor, customAccessor, selectMultipleAccessor
|
||||
])).toEqual(customAccessor);
|
||||
});
|
||||
|
||||
it('should throw when more than one custom accessor is provided', () => {
|
||||
@ -116,13 +131,13 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
it('should compose functions', () => {
|
||||
const dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true});
|
||||
const dummy2 = (_: any /** TODO #9100 */) => ({'dummy2': true});
|
||||
const v = composeValidators([dummy1, dummy2]) !;
|
||||
const v = composeValidators([dummy1, dummy2])!;
|
||||
expect(v(new FormControl(''))).toEqual({'dummy1': true, 'dummy2': true});
|
||||
});
|
||||
|
||||
it('should compose validator directives', () => {
|
||||
const dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true});
|
||||
const v = composeValidators([dummy1, new CustomValidatorDirective()]) !;
|
||||
const v = composeValidators([dummy1, new CustomValidatorDirective()])!;
|
||||
expect(v(new FormControl(''))).toEqual({'dummy1': true, 'custom': true});
|
||||
});
|
||||
});
|
||||
@ -137,8 +152,8 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
form = new FormGroupDirective([], []);
|
||||
formModel = new FormGroup({
|
||||
'login': new FormControl(),
|
||||
'passwords': new FormGroup(
|
||||
{'password': new FormControl(), 'passwordConfirm': new FormControl()})
|
||||
'passwords':
|
||||
new FormGroup({'password': new FormControl(), 'passwordConfirm': new FormControl()})
|
||||
});
|
||||
form.form = formModel;
|
||||
|
||||
@ -174,7 +189,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
|
||||
describe('addControl', () => {
|
||||
it('should throw when no control found', () => {
|
||||
const dir = new FormControlName(form, null !, null !, [defaultAccessor], null);
|
||||
const dir = new FormControlName(form, null!, null!, [defaultAccessor], null);
|
||||
dir.name = 'invalidName';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
@ -182,7 +197,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
});
|
||||
|
||||
it('should throw for a named control when no value accessor', () => {
|
||||
const dir = new FormControlName(form, null !, null !, null !, null);
|
||||
const dir = new FormControlName(form, null!, null!, null!, null);
|
||||
dir.name = 'login';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
@ -190,8 +205,8 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
});
|
||||
|
||||
it('should throw when no value accessor with path', () => {
|
||||
const group = new FormGroupName(form, null !, null !);
|
||||
const dir = new FormControlName(group, null !, null !, null !, null);
|
||||
const group = new FormGroupName(form, null!, null!);
|
||||
const dir = new FormControlName(group, null!, null!, null!, null);
|
||||
group.name = 'passwords';
|
||||
dir.name = 'password';
|
||||
|
||||
@ -321,7 +336,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
personControlGroupDir = new NgModelGroup(form, [], []);
|
||||
personControlGroupDir.name = 'person';
|
||||
|
||||
loginControlDir = new NgModel(personControlGroupDir, null !, null !, [defaultAccessor]);
|
||||
loginControlDir = new NgModel(personControlGroupDir, null!, null!, [defaultAccessor]);
|
||||
loginControlDir.name = 'login';
|
||||
loginControlDir.valueAccessor = new DummyControlValueAccessor();
|
||||
});
|
||||
@ -509,7 +524,9 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
controlDir.form = control;
|
||||
});
|
||||
|
||||
it('should reexport control properties', () => { checkProperties(control); });
|
||||
it('should reexport control properties', () => {
|
||||
checkProperties(control);
|
||||
});
|
||||
|
||||
it('should reexport control methods', () => {
|
||||
expect(controlDir.hasError('required')).toBe(control.hasError('required'));
|
||||
@ -544,7 +561,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
|
||||
beforeEach(() => {
|
||||
ngModel = new NgModel(
|
||||
null !, [Validators.required], [asyncValidator('expected')], [defaultAccessor]);
|
||||
null!, [Validators.required], [asyncValidator('expected')], [defaultAccessor]);
|
||||
ngModel.valueAccessor = new DummyControlValueAccessor();
|
||||
control = ngModel.control;
|
||||
});
|
||||
@ -577,7 +594,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
});
|
||||
|
||||
it('should throw when no value accessor with named control', () => {
|
||||
const namedDir = new NgModel(null !, null !, null !, null !);
|
||||
const namedDir = new NgModel(null!, null!, null!, null!);
|
||||
namedDir.name = 'one';
|
||||
|
||||
expect(() => namedDir.ngOnChanges({}))
|
||||
@ -585,7 +602,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
});
|
||||
|
||||
it('should throw when no value accessor with unnamed control', () => {
|
||||
const unnamedDir = new NgModel(null !, null !, null !, null !);
|
||||
const unnamedDir = new NgModel(null!, null!, null!, null!);
|
||||
|
||||
expect(() => unnamedDir.ngOnChanges({}))
|
||||
.toThrowError(
|
||||
@ -641,7 +658,6 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
ngModel.ngOnChanges({isDisabled: new SimpleChange(null, 'anything else', false)});
|
||||
tick();
|
||||
expect(ngModel.control.disabled).toEqual(true);
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
@ -656,7 +672,7 @@ function asyncValidator(expected: any, timeout = 0) {
|
||||
parent.form = new FormGroup({'name': formModel});
|
||||
controlNameDir = new FormControlName(parent, [], [], [defaultAccessor], null);
|
||||
controlNameDir.name = 'name';
|
||||
(controlNameDir as{control: FormControl}).control = formModel;
|
||||
(controlNameDir as {control: FormControl}).control = formModel;
|
||||
});
|
||||
|
||||
it('should reexport control properties', () => {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,195 +8,208 @@
|
||||
import {fakeAsync, tick} from '@angular/core/testing';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {FormBuilder, Validators} from '@angular/forms';
|
||||
import {of } from 'rxjs';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
(function() {
|
||||
function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ { return null; }
|
||||
function asyncValidator(_: any /** TODO #9100 */) { return Promise.resolve(null); }
|
||||
function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ {
|
||||
return null;
|
||||
}
|
||||
function asyncValidator(_: any /** TODO #9100 */) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
describe('Form Builder', () => {
|
||||
let b: FormBuilder;
|
||||
describe('Form Builder', () => {
|
||||
let b: FormBuilder;
|
||||
|
||||
beforeEach(() => { b = new FormBuilder(); });
|
||||
beforeEach(() => {
|
||||
b = new FormBuilder();
|
||||
});
|
||||
|
||||
it('should create controls from a value', () => {
|
||||
const g = b.group({'login': 'some value'});
|
||||
it('should create controls from a value', () => {
|
||||
const g = b.group({'login': 'some value'});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should create controls from a boxed value', () => {
|
||||
const g = b.group({'login': {value: 'some value', disabled: true}});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('should create controls from an array', () => {
|
||||
const g = b.group(
|
||||
{'login': ['some value'], 'password': ['some value', syncValidator, asyncValidator]});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['password'].value).toEqual('some value');
|
||||
expect(g.controls['password'].validator).toEqual(syncValidator);
|
||||
expect(g.controls['password'].asyncValidator).toEqual(asyncValidator);
|
||||
});
|
||||
|
||||
it('should use controls whose form state is a primitive value', () => {
|
||||
const g = b.group({'login': b.control('some value', syncValidator, asyncValidator)});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should support controls with no validators and whose form state is null', () => {
|
||||
const g = b.group({'login': b.control(null)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBeNull();
|
||||
expect(g.controls['login'].asyncValidator).toBeNull();
|
||||
});
|
||||
|
||||
it('should support controls with validators and whose form state is null', () => {
|
||||
const g = b.group({'login': b.control(null, syncValidator, asyncValidator)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should support controls with no validators and whose form state is undefined', () => {
|
||||
const g = b.group({'login': b.control(undefined)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBeNull();
|
||||
expect(g.controls['login'].asyncValidator).toBeNull();
|
||||
});
|
||||
|
||||
it('should support controls with validators and whose form state is undefined', () => {
|
||||
const g = b.group({'login': b.control(undefined, syncValidator, asyncValidator)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create groups with a custom validator', () => {
|
||||
const g = b.group(
|
||||
{'login': 'some value'}, {'validator': syncValidator, 'asyncValidator': asyncValidator});
|
||||
|
||||
expect(g.validator).toBe(syncValidator);
|
||||
expect(g.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create control arrays', () => {
|
||||
const c = b.control('three');
|
||||
const e = b.control(null);
|
||||
const f = b.control(undefined);
|
||||
const a = b.array(
|
||||
['one', ['two', syncValidator], c, b.array(['four']), e, f], syncValidator, asyncValidator);
|
||||
|
||||
expect(a.value).toEqual(['one', 'two', 'three', ['four'], null, null]);
|
||||
expect(a.validator).toBe(syncValidator);
|
||||
expect(a.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create control arrays with multiple async validators', fakeAsync(() => {
|
||||
function asyncValidator1() {
|
||||
return of({'async1': true});
|
||||
}
|
||||
function asyncValidator2() {
|
||||
return of({'async2': true});
|
||||
}
|
||||
|
||||
const a = b.array(['one', 'two'], null, [asyncValidator1, asyncValidator2]);
|
||||
expect(a.value).toEqual(['one', 'two']);
|
||||
|
||||
tick();
|
||||
|
||||
expect(a.errors).toEqual({'async1': true, 'async2': true});
|
||||
}));
|
||||
|
||||
it('should create control arrays with multiple sync validators', () => {
|
||||
function syncValidator1() {
|
||||
return {'sync1': true};
|
||||
}
|
||||
function syncValidator2() {
|
||||
return {'sync2': true};
|
||||
}
|
||||
|
||||
const a = b.array(['one', 'two'], [syncValidator1, syncValidator2]);
|
||||
expect(a.value).toEqual(['one', 'two']);
|
||||
expect(a.errors).toEqual({'sync1': true, 'sync2': true});
|
||||
});
|
||||
|
||||
describe('updateOn', () => {
|
||||
it('should default to on change', () => {
|
||||
const c = b.control('');
|
||||
expect(c.updateOn).toEqual('change');
|
||||
});
|
||||
|
||||
it('should create controls from a boxed value', () => {
|
||||
const g = b.group({'login': {value: 'some value', disabled: true}});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].disabled).toEqual(true);
|
||||
it('should default to on change with an options obj', () => {
|
||||
const c = b.control('', {validators: Validators.required});
|
||||
expect(c.updateOn).toEqual('change');
|
||||
});
|
||||
|
||||
it('should create controls from an array', () => {
|
||||
const g = b.group(
|
||||
{'login': ['some value'], 'password': ['some value', syncValidator, asyncValidator]});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['password'].value).toEqual('some value');
|
||||
expect(g.controls['password'].validator).toEqual(syncValidator);
|
||||
expect(g.controls['password'].asyncValidator).toEqual(asyncValidator);
|
||||
it('should set updateOn when updating on blur', () => {
|
||||
const c = b.control('', {updateOn: 'blur'});
|
||||
expect(c.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should use controls whose form state is a primitive value', () => {
|
||||
const g = b.group({'login': b.control('some value', syncValidator, asyncValidator)});
|
||||
describe('in groups and arrays', () => {
|
||||
it('should default to group updateOn when not set in control', () => {
|
||||
const g = b.group({one: b.control(''), two: b.control('')}, {updateOn: 'blur'});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should support controls with no validators and whose form state is null', () => {
|
||||
const g = b.group({'login': b.control(null)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBeNull();
|
||||
expect(g.controls['login'].asyncValidator).toBeNull();
|
||||
});
|
||||
|
||||
it('should support controls with validators and whose form state is null', () => {
|
||||
const g = b.group({'login': b.control(null, syncValidator, asyncValidator)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should support controls with no validators and whose form state is undefined', () => {
|
||||
const g = b.group({'login': b.control(undefined)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBeNull();
|
||||
expect(g.controls['login'].asyncValidator).toBeNull();
|
||||
});
|
||||
|
||||
it('should support controls with validators and whose form state is undefined', () => {
|
||||
const g = b.group({'login': b.control(undefined, syncValidator, asyncValidator)});
|
||||
expect(g.controls['login'].value).toBeNull();
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create groups with a custom validator', () => {
|
||||
const g = b.group(
|
||||
{'login': 'some value'}, {'validator': syncValidator, 'asyncValidator': asyncValidator});
|
||||
|
||||
expect(g.validator).toBe(syncValidator);
|
||||
expect(g.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create control arrays', () => {
|
||||
const c = b.control('three');
|
||||
const e = b.control(null);
|
||||
const f = b.control(undefined);
|
||||
const a = b.array(
|
||||
['one', ['two', syncValidator], c, b.array(['four']), e, f], syncValidator,
|
||||
asyncValidator);
|
||||
|
||||
expect(a.value).toEqual(['one', 'two', 'three', ['four'], null, null]);
|
||||
expect(a.validator).toBe(syncValidator);
|
||||
expect(a.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create control arrays with multiple async validators', fakeAsync(() => {
|
||||
function asyncValidator1() { return of ({'async1': true}); }
|
||||
function asyncValidator2() { return of ({'async2': true}); }
|
||||
|
||||
const a = b.array(['one', 'two'], null, [asyncValidator1, asyncValidator2]);
|
||||
expect(a.value).toEqual(['one', 'two']);
|
||||
|
||||
tick();
|
||||
|
||||
expect(a.errors).toEqual({'async1': true, 'async2': true});
|
||||
}));
|
||||
|
||||
it('should create control arrays with multiple sync validators', () => {
|
||||
function syncValidator1() { return {'sync1': true}; }
|
||||
function syncValidator2() { return {'sync2': true}; }
|
||||
|
||||
const a = b.array(['one', 'two'], [syncValidator1, syncValidator2]);
|
||||
expect(a.value).toEqual(['one', 'two']);
|
||||
expect(a.errors).toEqual({'sync1': true, 'sync2': true});
|
||||
});
|
||||
|
||||
describe('updateOn', () => {
|
||||
it('should default to on change', () => {
|
||||
const c = b.control('');
|
||||
expect(c.updateOn).toEqual('change');
|
||||
expect(g.get('one')!.updateOn).toEqual('blur');
|
||||
expect(g.get('two')!.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should default to on change with an options obj', () => {
|
||||
const c = b.control('', {validators: Validators.required});
|
||||
expect(c.updateOn).toEqual('change');
|
||||
it('should default to array updateOn when not set in control', () => {
|
||||
const a = b.array([b.control(''), b.control('')], {updateOn: 'blur'});
|
||||
|
||||
expect(a.get([0])!.updateOn).toEqual('blur');
|
||||
expect(a.get([1])!.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should set updateOn when updating on blur', () => {
|
||||
const c = b.control('', {updateOn: 'blur'});
|
||||
expect(c.updateOn).toEqual('blur');
|
||||
it('should set updateOn with nested groups', () => {
|
||||
const g = b.group(
|
||||
{
|
||||
group: b.group({one: b.control(''), two: b.control('')}),
|
||||
},
|
||||
{updateOn: 'blur'});
|
||||
|
||||
expect(g.get('group.one')!.updateOn).toEqual('blur');
|
||||
expect(g.get('group.two')!.updateOn).toEqual('blur');
|
||||
expect(g.get('group')!.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
describe('in groups and arrays', () => {
|
||||
it('should default to group updateOn when not set in control', () => {
|
||||
const g = b.group({one: b.control(''), two: b.control('')}, {updateOn: 'blur'});
|
||||
it('should set updateOn with nested arrays', () => {
|
||||
const g = b.group(
|
||||
{
|
||||
arr: b.array([b.control(''), b.control('')]),
|
||||
},
|
||||
{updateOn: 'blur'});
|
||||
|
||||
expect(g.get('one') !.updateOn).toEqual('blur');
|
||||
expect(g.get('two') !.updateOn).toEqual('blur');
|
||||
expect(g.get(['arr', 0])!.updateOn).toEqual('blur');
|
||||
expect(g.get(['arr', 1])!.updateOn).toEqual('blur');
|
||||
expect(g.get('arr')!.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should allow control updateOn to override group updateOn', () => {
|
||||
const g = b.group(
|
||||
{one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'});
|
||||
|
||||
expect(g.get('one')!.updateOn).toEqual('change');
|
||||
expect(g.get('two')!.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should set updateOn with complex setup', () => {
|
||||
const g = b.group({
|
||||
group: b.group(
|
||||
{one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'}),
|
||||
groupTwo: b.group({one: b.control('')}, {updateOn: 'submit'}),
|
||||
three: b.control('')
|
||||
});
|
||||
|
||||
it('should default to array updateOn when not set in control', () => {
|
||||
const a = b.array([b.control(''), b.control('')], {updateOn: 'blur'});
|
||||
|
||||
expect(a.get([0]) !.updateOn).toEqual('blur');
|
||||
expect(a.get([1]) !.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should set updateOn with nested groups', () => {
|
||||
const g = b.group(
|
||||
{
|
||||
group: b.group({one: b.control(''), two: b.control('')}),
|
||||
},
|
||||
{updateOn: 'blur'});
|
||||
|
||||
expect(g.get('group.one') !.updateOn).toEqual('blur');
|
||||
expect(g.get('group.two') !.updateOn).toEqual('blur');
|
||||
expect(g.get('group') !.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should set updateOn with nested arrays', () => {
|
||||
const g = b.group(
|
||||
{
|
||||
arr: b.array([b.control(''), b.control('')]),
|
||||
},
|
||||
{updateOn: 'blur'});
|
||||
|
||||
expect(g.get(['arr', 0]) !.updateOn).toEqual('blur');
|
||||
expect(g.get(['arr', 1]) !.updateOn).toEqual('blur');
|
||||
expect(g.get('arr') !.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should allow control updateOn to override group updateOn', () => {
|
||||
const g = b.group(
|
||||
{one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'});
|
||||
|
||||
expect(g.get('one') !.updateOn).toEqual('change');
|
||||
expect(g.get('two') !.updateOn).toEqual('blur');
|
||||
});
|
||||
|
||||
it('should set updateOn with complex setup', () => {
|
||||
const g = b.group({
|
||||
group: b.group(
|
||||
{one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'}),
|
||||
groupTwo: b.group({one: b.control('')}, {updateOn: 'submit'}),
|
||||
three: b.control('')
|
||||
});
|
||||
|
||||
expect(g.get('group.one') !.updateOn).toEqual('change');
|
||||
expect(g.get('group.two') !.updateOn).toEqual('blur');
|
||||
expect(g.get('groupTwo.one') !.updateOn).toEqual('submit');
|
||||
expect(g.get('three') !.updateOn).toEqual('change');
|
||||
});
|
||||
expect(g.get('group.one')!.updateOn).toEqual('change');
|
||||
expect(g.get('group.two')!.updateOn).toEqual('blur');
|
||||
expect(g.get('groupTwo.one')!.updateOn).toEqual('submit');
|
||||
expect(g.get('three')!.updateOn).toEqual('change');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import {ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {Component, Directive, Input, Type, forwardRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {Component, Directive, forwardRef, Input, Type} from '@angular/core';
|
||||
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
import {AbstractControl, AsyncValidator, AsyncValidatorFn, COMPOSITION_BUFFER_MODE, FormArray, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util';
|
||||
@ -19,7 +19,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
{
|
||||
describe('reactive forms integration tests', () => {
|
||||
|
||||
function initTest<T>(component: Type<T>, ...directives: Type<any>[]): ComponentFixture<T> {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [component, ...directives], imports: [FormsModule, ReactiveFormsModule]});
|
||||
@ -74,11 +73,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(form.value).toEqual({'login': 'updatedValue'});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('re-bound form groups', () => {
|
||||
|
||||
it('should update DOM elements initially', () => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
fixture.componentInstance.form = new FormGroup({'login': new FormControl('oldValue')});
|
||||
@ -150,7 +147,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
});
|
||||
fixture.componentInstance.form = form;
|
||||
fixture.detectChanges();
|
||||
expect(form.get('login') !.errors).toEqual({required: true});
|
||||
expect(form.get('login')!.errors).toEqual({required: true});
|
||||
|
||||
const newForm = new FormGroup({
|
||||
'login': new FormControl(''),
|
||||
@ -161,34 +158,31 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
fixture.componentInstance.form = newForm;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(newForm.get('login') !.errors).toEqual({required: true});
|
||||
expect(newForm.get('login')!.errors).toEqual({required: true});
|
||||
});
|
||||
|
||||
it('should pick up dir validators from nested form groups', () => {
|
||||
const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator);
|
||||
const form = new FormGroup({
|
||||
'signin':
|
||||
new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
});
|
||||
fixture.componentInstance.form = form;
|
||||
fixture.detectChanges();
|
||||
expect(form.get('signin') !.valid).toBe(false);
|
||||
expect(form.get('signin')!.valid).toBe(false);
|
||||
|
||||
const newForm = new FormGroup({
|
||||
'signin':
|
||||
new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
});
|
||||
fixture.componentInstance.form = newForm;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.get('signin') !.valid).toBe(false);
|
||||
expect(form.get('signin')!.valid).toBe(false);
|
||||
});
|
||||
|
||||
it('should strip named controls that are not found', () => {
|
||||
const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator);
|
||||
const form = new FormGroup({
|
||||
'signin':
|
||||
new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
});
|
||||
fixture.componentInstance.form = form;
|
||||
fixture.detectChanges();
|
||||
@ -200,8 +194,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(emailInput.nativeElement.value).toEqual('email');
|
||||
|
||||
const newForm = new FormGroup({
|
||||
'signin':
|
||||
new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')})
|
||||
});
|
||||
fixture.componentInstance.form = newForm;
|
||||
fixture.detectChanges();
|
||||
@ -237,7 +230,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
});
|
||||
|
||||
describe('nested control rebinding', () => {
|
||||
|
||||
it('should attach dir to control when leaf control changes', () => {
|
||||
const form = new FormGroup({'login': new FormControl('oldValue')});
|
||||
const fixture = initTest(FormGroupComp);
|
||||
@ -359,7 +351,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(newArr.value).toEqual(['last one']);
|
||||
|
||||
newArr.get([0]) !.setValue('set value');
|
||||
newArr.get([0])!.setValue('set value');
|
||||
fixture.detectChanges();
|
||||
|
||||
firstInput = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
@ -416,14 +408,12 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(newArr.value).toEqual(['SF', 'LA', 'Tulsa']);
|
||||
|
||||
newArr.get([2]) !.setValue('NY');
|
||||
newArr.get([2])!.setValue('NY');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(lastInput.value).toEqual('NY');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('form arrays', () => {
|
||||
@ -494,7 +484,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
cities: [{town: 'LA', state: 'CA'}, {town: 'NY', state: 'NY'}]
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('programmatic changes', () => {
|
||||
@ -592,13 +581,10 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
const input = fixture.debugElement.query(By.css('my-input'));
|
||||
expect(input.nativeElement.getAttribute('disabled')).toBe(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('user input', () => {
|
||||
|
||||
it('should mark controls as touched after interacting with the DOM control', () => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
const login = new FormControl('oldValue');
|
||||
@ -613,14 +599,13 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(login.touched).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('submit and reset events', () => {
|
||||
it('should emit ngSubmit event with the original submit event on submit', () => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')});
|
||||
fixture.componentInstance.event = null !;
|
||||
fixture.componentInstance.event = null!;
|
||||
fixture.detectChanges();
|
||||
|
||||
const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||
@ -672,18 +657,18 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
form.reset();
|
||||
expect(loginEl.value).toBe('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('value changes and status changes', () => {
|
||||
|
||||
it('should mark controls as dirty before emitting a value change event', () => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
const login = new FormControl('oldValue');
|
||||
fixture.componentInstance.form = new FormGroup({'login': login});
|
||||
fixture.detectChanges();
|
||||
|
||||
login.valueChanges.subscribe(() => { expect(login.dirty).toBe(true); });
|
||||
login.valueChanges.subscribe(() => {
|
||||
expect(login.dirty).toBe(true);
|
||||
});
|
||||
|
||||
const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
loginEl.value = 'newValue';
|
||||
@ -705,11 +690,12 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(login.pristine).toBe(false);
|
||||
|
||||
login.valueChanges.subscribe(() => { expect(login.pristine).toBe(true); });
|
||||
login.valueChanges.subscribe(() => {
|
||||
expect(login.pristine).toBe(true);
|
||||
});
|
||||
|
||||
form.reset();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('setting status classes', () => {
|
||||
@ -736,7 +722,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
it('should work with single fields and async validators', fakeAsync(() => {
|
||||
const fixture = initTest(FormControlComp);
|
||||
const control = new FormControl('', null !, uniqLoginAsyncValidator('good'));
|
||||
const control = new FormControl('', null!, uniqLoginAsyncValidator('good'));
|
||||
fixture.debugElement.componentInstance.control = control;
|
||||
fixture.detectChanges();
|
||||
|
||||
@ -831,13 +817,10 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(sortedClassList(formEl)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('updateOn options', () => {
|
||||
|
||||
describe('on blur', () => {
|
||||
|
||||
it('should not update value or validity based on user input until blur', () => {
|
||||
const fixture = initTest(FormControlComp);
|
||||
const control = new FormControl('', {validators: Validators.required, updateOn: 'blur'});
|
||||
@ -1141,7 +1124,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(control.value)
|
||||
.toEqual('Nancy', 'Expected value to change once control is blurred.');
|
||||
expect(control.valid).toBe(true, 'Expected validation to run once control is blurred.');
|
||||
|
||||
});
|
||||
|
||||
it('should update on blur with array updateOn', () => {
|
||||
@ -1167,7 +1149,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(control.value)
|
||||
.toEqual('Nancy', 'Expected value to change once control is blurred.');
|
||||
expect(control.valid).toBe(true, 'Expected validation to run once control is blurred.');
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -1206,17 +1187,13 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(passwordControl.valid)
|
||||
.toBe(true, 'Expected validation to run once control is blurred.');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('on submit', () => {
|
||||
|
||||
it('should set initial value and validity on init', () => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
const form = new FormGroup({
|
||||
login:
|
||||
new FormControl('Nancy', {validators: Validators.required, updateOn: 'submit'})
|
||||
login: new FormControl('Nancy', {validators: Validators.required, updateOn: 'submit'})
|
||||
});
|
||||
fixture.componentInstance.form = form;
|
||||
fixture.detectChanges();
|
||||
@ -1430,7 +1407,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
fixture.componentInstance.form = formGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
const values: (string | {[key: string]: string})[] = [];
|
||||
const values: (string|{[key: string]: string})[] = [];
|
||||
const streams = merge(
|
||||
control.valueChanges, control.statusChanges, formGroup.valueChanges,
|
||||
formGroup.statusChanges);
|
||||
@ -1493,8 +1470,8 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
fixture.componentInstance.form = formGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
formGroup.get('signin.login') !.setValidators(validatorSpy);
|
||||
formGroup.get('signin') !.setValidators(groupValidatorSpy);
|
||||
formGroup.get('signin.login')!.setValidators(validatorSpy);
|
||||
formGroup.get('signin')!.setValidators(groupValidatorSpy);
|
||||
|
||||
const form = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||
dispatchEvent(form, 'submit');
|
||||
@ -1502,7 +1479,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(validatorSpy).not.toHaveBeenCalled();
|
||||
expect(groupValidatorSpy).not.toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
it('should mark as untouched properly if pending touched', () => {
|
||||
@ -1554,7 +1530,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(control.value).toEqual('Nancy', 'Expected value to change on submit.');
|
||||
expect(control.valid).toBe(true, 'Expected validation to run on submit.');
|
||||
|
||||
});
|
||||
|
||||
it('should update on submit with array updateOn', () => {
|
||||
@ -1581,7 +1556,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(control.value).toEqual('Nancy', 'Expected value to change once control on submit');
|
||||
expect(control.valid).toBe(true, 'Expected validation to run on submit.');
|
||||
|
||||
});
|
||||
|
||||
it('should allow child control updateOn submit to override group updateOn', () => {
|
||||
@ -1619,9 +1593,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(passwordControl.value).toEqual('Carson', 'Expected value to change on submit.');
|
||||
expect(passwordControl.valid).toBe(true, 'Expected validation to run on submit.');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ngModel interactions', () => {
|
||||
@ -1636,7 +1608,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
});
|
||||
|
||||
describe('deprecation warnings', () => {
|
||||
|
||||
it('should warn once by default when using ngModel with formControlName', fakeAsync(() => {
|
||||
const fixture = initTest(FormGroupNgModel);
|
||||
fixture.componentInstance.form =
|
||||
@ -1679,8 +1650,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FormControlNgModel],
|
||||
imports:
|
||||
[ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'always'})]
|
||||
imports: [ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'always'})]
|
||||
});
|
||||
|
||||
const fixture = TestBed.createComponent(FormControlNgModel);
|
||||
@ -1710,7 +1680,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
it('should support ngModel for complex forms', fakeAsync(() => {
|
||||
@ -1794,9 +1763,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
|
||||
expect(fixture.componentInstance.login)
|
||||
.toEqual('Nancy', 'Expected ngModel value to update on submit.');
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('validations', () => {
|
||||
@ -1969,9 +1936,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
.toEqual(pattern.nativeElement.getAttribute('pattern'));
|
||||
|
||||
fixture.componentInstance.required = false;
|
||||
fixture.componentInstance.minLen = null !;
|
||||
fixture.componentInstance.maxLen = null !;
|
||||
fixture.componentInstance.pattern = null !;
|
||||
fixture.componentInstance.minLen = null!;
|
||||
fixture.componentInstance.maxLen = null!;
|
||||
fixture.componentInstance.pattern = null!;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.hasError('required', ['login'])).toEqual(false);
|
||||
@ -2011,9 +1978,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.required = false;
|
||||
fixture.componentInstance.minLen = null !;
|
||||
fixture.componentInstance.maxLen = null !;
|
||||
fixture.componentInstance.pattern = null !;
|
||||
fixture.componentInstance.minLen = null!;
|
||||
fixture.componentInstance.maxLen = null!;
|
||||
fixture.componentInstance.pattern = null!;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(newForm.hasError('required', ['login'])).toEqual(false);
|
||||
@ -2111,7 +2078,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
const fixture = initTest(FormControlComp);
|
||||
const resultArr: number[] = [];
|
||||
fixture.componentInstance.control =
|
||||
new FormControl('', null !, observableValidator(resultArr));
|
||||
new FormControl('', null!, observableValidator(resultArr));
|
||||
fixture.detectChanges();
|
||||
tick(100);
|
||||
|
||||
@ -2130,12 +2097,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(resultArr.length)
|
||||
.toEqual(2, `Expected original observable to be canceled on the next value change.`);
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
||||
it('should throw if a form isn\'t passed into formGroup', () => {
|
||||
const fixture = initTest(FormGroupComp);
|
||||
|
||||
@ -2335,11 +2299,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(new RegExp('If you define both a name and a formControlName'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('IME events', () => {
|
||||
|
||||
it('should determine IME event handling depending on platform by default', () => {
|
||||
const fixture = initTest(FormControlComp);
|
||||
fixture.componentInstance.control = new FormControl('oldValue');
|
||||
@ -2417,16 +2379,16 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
|
||||
// formControl should update normally
|
||||
expect(fixture.componentInstance.control.value).toEqual('updatedValue');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function uniqLoginAsyncValidator(expectedValue: string, timeout: number = 0) {
|
||||
return (c: AbstractControl) => {
|
||||
let resolve: (result: any) => void;
|
||||
const promise = new Promise<any>(res => { resolve = res; });
|
||||
const promise = new Promise<any>(res => {
|
||||
resolve = res;
|
||||
});
|
||||
const res = (c.value == expectedValue) ? null : {'uniqLogin': true};
|
||||
setTimeout(() => resolve(res), timeout);
|
||||
return promise;
|
||||
@ -2452,22 +2414,22 @@ class LoginIsEmptyValidator {
|
||||
|
||||
@Directive({
|
||||
selector: '[uniq-login-validator]',
|
||||
providers: [{
|
||||
provide: NG_ASYNC_VALIDATORS,
|
||||
useExisting: forwardRef(() => UniqLoginValidator),
|
||||
multi: true
|
||||
}]
|
||||
providers: [
|
||||
{provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => UniqLoginValidator), multi: true}
|
||||
]
|
||||
})
|
||||
class UniqLoginValidator implements AsyncValidator {
|
||||
@Input('uniq-login-validator') expected: any;
|
||||
|
||||
validate(c: AbstractControl) { return uniqLoginAsyncValidator(this.expected)(c); }
|
||||
validate(c: AbstractControl) {
|
||||
return uniqLoginAsyncValidator(this.expected)(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'form-control-comp', template: `<input type="text" [formControl]="control">`})
|
||||
class FormControlComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2479,11 +2441,11 @@ class FormControlComp {
|
||||
})
|
||||
class FormGroupComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
event !: Event;
|
||||
event!: Event;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2499,7 +2461,7 @@ class FormGroupComp {
|
||||
})
|
||||
class NestedFormGroupComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2515,9 +2477,9 @@ class NestedFormGroupComp {
|
||||
})
|
||||
class FormArrayComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
cityArray !: FormArray;
|
||||
cityArray!: FormArray;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2534,9 +2496,9 @@ class FormArrayComp {
|
||||
})
|
||||
class FormArrayNestedGroup {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
cityArray !: FormArray;
|
||||
cityArray!: FormArray;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2549,11 +2511,11 @@ class FormArrayNestedGroup {
|
||||
})
|
||||
class FormGroupNgModel {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
login !: string;
|
||||
login!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
password !: string;
|
||||
password!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2565,13 +2527,13 @@ class FormGroupNgModel {
|
||||
})
|
||||
class FormControlNgModel {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
login !: string;
|
||||
login!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
passwordControl !: FormControl;
|
||||
passwordControl!: FormControl;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
password !: string;
|
||||
password!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2586,7 +2548,7 @@ class FormControlNgModel {
|
||||
})
|
||||
class LoginIsEmptyWrapper {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2601,15 +2563,15 @@ class LoginIsEmptyWrapper {
|
||||
})
|
||||
class ValidationBindingsForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
required !: boolean;
|
||||
required!: boolean;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
minLen !: number;
|
||||
minLen!: number;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
maxLen !: number;
|
||||
maxLen!: number;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
pattern !: string;
|
||||
pattern!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2618,7 +2580,7 @@ class ValidationBindingsForm {
|
||||
})
|
||||
class FormControlCheckboxRequiredValidator {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2630,5 +2592,5 @@ class FormControlCheckboxRequiredValidator {
|
||||
})
|
||||
class UniqLoginWrapper {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
}
|
||||
|
@ -16,6 +16,10 @@ export class SpyChangeDetectorRef extends SpyObject {
|
||||
}
|
||||
}
|
||||
|
||||
export class SpyNgControl extends SpyObject { path = []; }
|
||||
export class SpyNgControl extends SpyObject {
|
||||
path = [];
|
||||
}
|
||||
|
||||
export class SpyValueAccessor extends SpyObject { writeValue: any; }
|
||||
export class SpyValueAccessor extends SpyObject {
|
||||
writeValue: any;
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import {ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {Component, Directive, Type, forwardRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {Component, Directive, forwardRef, Type} from '@angular/core';
|
||||
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, FormControl, FormsModule, NG_ASYNC_VALIDATORS, NgForm, NgModel} from '@angular/forms';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util';
|
||||
@ -18,7 +18,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
|
||||
{
|
||||
describe('template-driven forms integration tests', () => {
|
||||
|
||||
function initTest<T>(component: Type<T>, ...directives: Type<any>[]): ComponentFixture<T> {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [component, ...directives], imports: [FormsModule]});
|
||||
@ -57,7 +56,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
expect(form.valid).toBe(false);
|
||||
}));
|
||||
|
||||
it('should report properties which are written outside of template bindings', async() => {
|
||||
it('should report properties which are written outside of template bindings', async () => {
|
||||
// For example ngModel writes to `checked` property programmatically
|
||||
// (template does not contain binding to `checked` explicitly)
|
||||
// https://github.com/angular/angular/issues/33695
|
||||
@ -131,9 +130,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(form.control.get('name') !.value).toEqual({first: 'Nancy', last: 'Drew'});
|
||||
expect(form.control.get('name.first') !.value).toEqual('Nancy');
|
||||
expect(form.control.get('email') !.value).toEqual('some email');
|
||||
expect(form.control.get('name')!.value).toEqual({first: 'Nancy', last: 'Drew'});
|
||||
expect(form.control.get('name.first')!.value).toEqual('Nancy');
|
||||
expect(form.control.get('email')!.value).toEqual('some email');
|
||||
}));
|
||||
|
||||
it('should remove controls and control groups from form control model', fakeAsync(() => {
|
||||
@ -145,7 +144,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(form.control.get('email') !.value).toEqual('some email');
|
||||
expect(form.control.get('email')!.value).toEqual('some email');
|
||||
expect(form.value).toEqual({name: {first: 'Nancy'}, email: 'some email'});
|
||||
|
||||
// should remove individual control successfully
|
||||
@ -156,8 +155,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
expect(form.control.get('email')).toBe(null);
|
||||
expect(form.value).toEqual({name: {first: 'Nancy'}});
|
||||
|
||||
expect(form.control.get('name') !.value).toEqual({first: 'Nancy'});
|
||||
expect(form.control.get('name.first') !.value).toEqual('Nancy');
|
||||
expect(form.control.get('name')!.value).toEqual({first: 'Nancy'});
|
||||
expect(form.control.get('name.first')!.value).toEqual('Nancy');
|
||||
|
||||
// should remove form group successfully
|
||||
fixture.componentInstance.groupShowing = false;
|
||||
@ -192,7 +191,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
}));
|
||||
|
||||
it('should set status classes with ngModel and async validators', fakeAsync(() => {
|
||||
|
||||
const fixture = initTest(NgModelAsyncValidation, NgAsyncValidator);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
@ -252,7 +250,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
it('should not create a template-driven form when ngNoForm is used', () => {
|
||||
const fixture = initTest(NgNoFormComp);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children[0].providerTokens !.length).toEqual(0);
|
||||
expect(fixture.debugElement.children[0].providerTokens!.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should not add novalidate when ngNoForm is used', () => {
|
||||
@ -304,9 +302,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
});
|
||||
|
||||
describe('updateOn', () => {
|
||||
|
||||
describe('blur', () => {
|
||||
|
||||
it('should default updateOn to change', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelForm);
|
||||
fixture.componentInstance.name = '';
|
||||
@ -484,8 +480,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
const values: any[] = [];
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
|
||||
const sub = merge(form.valueChanges !, form.statusChanges !)
|
||||
.subscribe(val => values.push(val));
|
||||
const sub =
|
||||
merge(form.valueChanges!, form.statusChanges!).subscribe(val => values.push(val));
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
input.value = 'Nancy Drew';
|
||||
@ -560,13 +556,10 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
.toEqual(
|
||||
['fired', 'fired'],
|
||||
'Expected ngModelChanges to fire again on blur if value changed.');
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('submit', () => {
|
||||
|
||||
it('should set control updateOn to submit properly', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelForm);
|
||||
fixture.componentInstance.name = '';
|
||||
@ -700,8 +693,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
form.control.get('name') !.setValidators(groupValidatorSpy);
|
||||
form.control.get('name.last') !.setValidators(validatorSpy);
|
||||
form.control.get('name')!.setValidators(groupValidatorSpy);
|
||||
form.control.get('name.last')!.setValidators(validatorSpy);
|
||||
|
||||
const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||
dispatchEvent(formEl, 'submit');
|
||||
@ -816,8 +809,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
const values: any[] = [];
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
|
||||
const sub = merge(form.valueChanges !, form.statusChanges !)
|
||||
.subscribe(val => values.push(val));
|
||||
const sub =
|
||||
merge(form.valueChanges!, form.statusChanges!).subscribe(val => values.push(val));
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
input.value = 'Nancy Drew';
|
||||
@ -898,11 +891,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
['fired', 'fired'],
|
||||
'Expected ngModelChanges to fire again on submit if value changed.');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('ngFormOptions', () => {
|
||||
|
||||
it('should use ngFormOptions value when ngModelOptions are not set', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelOptionsStandalone);
|
||||
fixture.componentInstance.options = {name: 'two'};
|
||||
@ -911,12 +902,12 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
const controlOne = form.control.get('one') !as FormControl;
|
||||
const controlOne = form.control.get('one')! as FormControl;
|
||||
expect((controlOne as any)._updateOn).toBeUndefined();
|
||||
expect(controlOne.updateOn)
|
||||
.toEqual('blur', 'Expected first control to inherit updateOn from parent form.');
|
||||
|
||||
const controlTwo = form.control.get('two') !as FormControl;
|
||||
const controlTwo = form.control.get('two')! as FormControl;
|
||||
expect((controlTwo as any)._updateOn).toBeUndefined();
|
||||
expect(controlTwo.updateOn)
|
||||
.toEqual('blur', 'Expected last control to inherit updateOn from parent form.');
|
||||
@ -952,12 +943,12 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
const controlOne = form.control.get('one') !as FormControl;
|
||||
const controlOne = form.control.get('one')! as FormControl;
|
||||
expect((controlOne as any)._updateOn).toBeUndefined();
|
||||
expect(controlOne.updateOn)
|
||||
.toEqual('change', 'Expected control updateOn to inherit form updateOn.');
|
||||
|
||||
const controlTwo = form.control.get('two') !as FormControl;
|
||||
const controlTwo = form.control.get('two')! as FormControl;
|
||||
expect((controlTwo as any)._updateOn)
|
||||
.toEqual('blur', 'Expected control to set blur override.');
|
||||
expect(controlTwo.updateOn)
|
||||
@ -1016,15 +1007,13 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
expect(fixture.componentInstance.two)
|
||||
.toEqual('Nancy Drew', 'Expected standalone ngModel not to inherit blur update.');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('submit and reset events', () => {
|
||||
it('should emit ngSubmit event with the original submit event on submit', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelForm);
|
||||
fixture.componentInstance.event = null !;
|
||||
fixture.componentInstance.event = null!;
|
||||
|
||||
const form = fixture.debugElement.query(By.css('form'));
|
||||
dispatchEvent(form.nativeElement, 'submit');
|
||||
@ -1097,11 +1086,11 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
expect(form.valid).toEqual(true);
|
||||
expect(form.value).toEqual({});
|
||||
|
||||
let formValidity: string = undefined !;
|
||||
let formValue: Object = undefined !;
|
||||
let formValidity: string = undefined!;
|
||||
let formValue: Object = undefined!;
|
||||
|
||||
form.statusChanges !.subscribe((status: string) => formValidity = status);
|
||||
form.valueChanges !.subscribe((value: string) => formValue = value);
|
||||
form.statusChanges!.subscribe((status: string) => formValidity = status);
|
||||
form.valueChanges!.subscribe((value: string) => formValue = value);
|
||||
|
||||
tick();
|
||||
|
||||
@ -1116,8 +1105,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
form.get('name') !.valueChanges.subscribe(
|
||||
() => { expect(form.get('name') !.dirty).toBe(true); });
|
||||
form.get('name')!.valueChanges.subscribe(() => {
|
||||
expect(form.get('name')!.dirty).toBe(true);
|
||||
});
|
||||
|
||||
const inputEl = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
inputEl.value = 'newValue';
|
||||
@ -1138,10 +1128,11 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
inputEl.value = 'newValue';
|
||||
dispatchEvent(inputEl, 'input');
|
||||
|
||||
expect(form.get('name') !.pristine).toBe(false);
|
||||
expect(form.get('name')!.pristine).toBe(false);
|
||||
|
||||
form.get('name') !.valueChanges.subscribe(
|
||||
() => { expect(form.get('name') !.pristine).toBe(true); });
|
||||
form.get('name')!.valueChanges.subscribe(() => {
|
||||
expect(form.get('name')!.pristine).toBe(true);
|
||||
});
|
||||
|
||||
dispatchEvent(formEl, 'reset');
|
||||
}));
|
||||
@ -1160,7 +1151,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(form.value).toEqual({name: {first: '', last: 'Drew'}, email: 'some email'});
|
||||
expect(form.valid).toBe(false);
|
||||
expect(form.control.get('name.first') !.disabled).toBe(false);
|
||||
expect(form.control.get('name.first')!.disabled).toBe(false);
|
||||
|
||||
fixture.componentInstance.isDisabled = true;
|
||||
fixture.detectChanges();
|
||||
@ -1168,7 +1159,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
|
||||
expect(form.value).toEqual({name: {last: 'Drew'}, email: 'some email'});
|
||||
expect(form.valid).toBe(true);
|
||||
expect(form.control.get('name.first') !.disabled).toBe(true);
|
||||
expect(form.control.get('name.first')!.disabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('should add disabled attribute in the UI if disable() is called programmatically',
|
||||
@ -1180,7 +1171,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
form.control.get('name.first') !.disable();
|
||||
form.control.get('name.first')!.disable();
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
@ -1197,7 +1188,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(form.control.get('name') !.disabled).toBe(true);
|
||||
expect(form.control.get('name')!.disabled).toBe(true);
|
||||
|
||||
const customInput = fixture.debugElement.query(By.css('[name="custom"]'));
|
||||
expect(customInput.nativeElement.disabled).toEqual(true);
|
||||
@ -1219,7 +1210,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(form.control.get('name') !.disabled).toBe(true);
|
||||
expect(form.control.get('name')!.disabled).toBe(true);
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
expect(input.nativeElement.disabled).toEqual(true);
|
||||
@ -1229,18 +1220,16 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
expect(input.nativeElement.disabled).toEqual(false);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('validation directives', () => {
|
||||
|
||||
it('required validator should validate checkbox', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelCheckboxRequiredValidator);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const control =
|
||||
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !;
|
||||
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox')!;
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
expect(input.nativeElement.checked).toBe(false);
|
||||
@ -1276,7 +1265,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
tick();
|
||||
|
||||
const control =
|
||||
fixture.debugElement.children[0].injector.get(NgForm).control.get('email') !;
|
||||
fixture.debugElement.children[0].injector.get(NgForm).control.get('email')!;
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
expect(control.hasError('email')).toBe(false);
|
||||
@ -1476,9 +1465,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
.toEqual(pattern.nativeElement.getAttribute('pattern'));
|
||||
|
||||
fixture.componentInstance.required = false;
|
||||
fixture.componentInstance.minLen = null !;
|
||||
fixture.componentInstance.maxLen = null !;
|
||||
fixture.componentInstance.pattern = null !;
|
||||
fixture.componentInstance.minLen = null!;
|
||||
fixture.componentInstance.maxLen = null!;
|
||||
fixture.componentInstance.pattern = null!;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(form.control.hasError('required', ['required'])).toEqual(false);
|
||||
@ -1522,7 +1511,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
expect(onNgModelChange).toHaveBeenCalledTimes(2);
|
||||
tick();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('IME events', () => {
|
||||
@ -1611,7 +1599,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
// ngModel should update normally
|
||||
expect(fixture.componentInstance.name).toEqual('updatedValue');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('ngModel corner cases', () => {
|
||||
@ -1650,7 +1637,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
expect(() => fixture.detectChanges()).not.toThrowError();
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -1662,7 +1648,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
})
|
||||
class StandaloneNgModel {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
name !: string;
|
||||
name!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1675,9 +1661,9 @@ class StandaloneNgModel {
|
||||
})
|
||||
class NgModelForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
name !: string | null;
|
||||
name!: string|null;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
event !: Event;
|
||||
event!: Event;
|
||||
options = {};
|
||||
|
||||
onReset() {}
|
||||
@ -1701,13 +1687,13 @@ class NgModelNativeValidateForm {
|
||||
})
|
||||
class NgModelGroupForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
first !: string;
|
||||
first!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
last !: string;
|
||||
last!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
email !: string;
|
||||
email!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
isDisabled !: boolean;
|
||||
isDisabled!: boolean;
|
||||
options = {updateOn: 'change'};
|
||||
}
|
||||
|
||||
@ -1724,7 +1710,7 @@ class NgModelGroupForm {
|
||||
})
|
||||
class NgModelValidBinding {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
first !: string;
|
||||
first!: string;
|
||||
}
|
||||
|
||||
|
||||
@ -1741,11 +1727,11 @@ class NgModelValidBinding {
|
||||
})
|
||||
class NgModelNgIfForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
first !: string;
|
||||
first!: string;
|
||||
groupShowing = true;
|
||||
emailShowing = true;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
email !: string;
|
||||
email!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1781,9 +1767,9 @@ class InvalidNgModelNoName {
|
||||
})
|
||||
class NgModelOptionsStandalone {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
one !: string;
|
||||
one!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
two !: string;
|
||||
two!: string;
|
||||
options: {name?: string, standalone?: boolean, updateOn?: string} = {standalone: true};
|
||||
formOptions = {};
|
||||
}
|
||||
@ -1801,13 +1787,13 @@ class NgModelOptionsStandalone {
|
||||
})
|
||||
class NgModelValidationBindings {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
required !: boolean;
|
||||
required!: boolean;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
minLen !: number;
|
||||
minLen!: number;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
maxLen !: number;
|
||||
maxLen!: number;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
pattern !: string;
|
||||
pattern!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1820,11 +1806,11 @@ class NgModelValidationBindings {
|
||||
})
|
||||
class NgModelMultipleValidators {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
required !: boolean;
|
||||
required!: boolean;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
minLen !: number;
|
||||
minLen!: number;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
pattern !: string | RegExp;
|
||||
pattern!: string|RegExp;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1847,12 +1833,13 @@ class NgModelEmailValidator {
|
||||
|
||||
@Directive({
|
||||
selector: '[ng-async-validator]',
|
||||
providers: [
|
||||
{provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => NgAsyncValidator), multi: true}
|
||||
]
|
||||
providers:
|
||||
[{provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => NgAsyncValidator), multi: true}]
|
||||
})
|
||||
class NgAsyncValidator implements AsyncValidator {
|
||||
validate(c: AbstractControl) { return Promise.resolve(null); }
|
||||
validate(c: AbstractControl) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1873,11 +1860,13 @@ class NgModelAsyncValidation {
|
||||
})
|
||||
class NgModelChangesForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
name !: string;
|
||||
name!: string;
|
||||
events: string[] = [];
|
||||
options: any;
|
||||
|
||||
log() { this.events.push('fired'); }
|
||||
log() {
|
||||
this.events.push('fired');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -11,453 +11,487 @@ import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {AbstractControl, AsyncValidatorFn, FormArray, FormControl, Validators} from '@angular/forms';
|
||||
import {normalizeAsyncValidator} from '@angular/forms/src/directives/normalize_validator';
|
||||
import {AsyncValidator, ValidationErrors, ValidatorFn} from '@angular/forms/src/directives/validators';
|
||||
import {Observable, of , timer} from 'rxjs';
|
||||
import {Observable, of, timer} from 'rxjs';
|
||||
import {first, map} from 'rxjs/operators';
|
||||
|
||||
(function() {
|
||||
function validator(key: string, error: any): ValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
const r: ValidationErrors = {};
|
||||
r[key] = error;
|
||||
return r;
|
||||
};
|
||||
function validator(key: string, error: any): ValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
const r: ValidationErrors = {};
|
||||
r[key] = error;
|
||||
return r;
|
||||
};
|
||||
}
|
||||
|
||||
class AsyncValidatorDirective implements AsyncValidator {
|
||||
constructor(private expected: string, private error: any) {}
|
||||
|
||||
validate(c: any): Observable<ValidationErrors> {
|
||||
return Observable.create((obs: any) => {
|
||||
const error = this.expected !== c.value ? this.error : null;
|
||||
obs.next(error);
|
||||
obs.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncValidatorDirective implements AsyncValidator {
|
||||
constructor(private expected: string, private error: any) {}
|
||||
|
||||
validate(c: any): Observable<ValidationErrors> {
|
||||
return Observable.create((obs: any) => {
|
||||
const error = this.expected !== c.value ? this.error : null;
|
||||
obs.next(error);
|
||||
obs.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('Validators', () => {
|
||||
describe('min', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.min(2)(new FormControl(''))).toBeNull(); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.min(2)(new FormControl(null))).toBeNull(); });
|
||||
|
||||
it('should not error on undefined',
|
||||
() => { expect(Validators.min(2)(new FormControl(undefined))).toBeNull(); });
|
||||
|
||||
it('should return null if NaN after parsing',
|
||||
() => { expect(Validators.min(2)(new FormControl('a'))).toBeNull(); });
|
||||
|
||||
it('should return a validation error on small values', () => {
|
||||
expect(Validators.min(2)(new FormControl(1))).toEqual({'min': {'min': 2, 'actual': 1}});
|
||||
});
|
||||
|
||||
it('should return a validation error on small values converted from strings', () => {
|
||||
expect(Validators.min(2)(new FormControl('1'))).toEqual({'min': {'min': 2, 'actual': '1'}});
|
||||
});
|
||||
|
||||
it('should not error on big values',
|
||||
() => { expect(Validators.min(2)(new FormControl(3))).toBeNull(); });
|
||||
|
||||
it('should not error on equal values',
|
||||
() => { expect(Validators.min(2)(new FormControl(2))).toBeNull(); });
|
||||
|
||||
it('should not error on equal values when value is string',
|
||||
() => { expect(Validators.min(2)(new FormControl('2'))).toBeNull(); });
|
||||
|
||||
it('should validate as expected when min value is a string', () => {
|
||||
expect(Validators.min('2' as any)(new FormControl(1))).toEqual({
|
||||
'min': {'min': '2', 'actual': 1}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if min value is undefined',
|
||||
() => { expect(Validators.min(undefined as any)(new FormControl(3))).toBeNull(); });
|
||||
|
||||
it('should return null if min value is null',
|
||||
() => { expect(Validators.min(null as any)(new FormControl(3))).toBeNull(); });
|
||||
describe('Validators', () => {
|
||||
describe('min', () => {
|
||||
it('should not error on an empty string', () => {
|
||||
expect(Validators.min(2)(new FormControl(''))).toBeNull();
|
||||
});
|
||||
|
||||
describe('max', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.max(2)(new FormControl(''))).toBeNull(); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.max(2)(new FormControl(null))).toBeNull(); });
|
||||
|
||||
it('should not error on undefined',
|
||||
() => { expect(Validators.max(2)(new FormControl(undefined))).toBeNull(); });
|
||||
|
||||
it('should return null if NaN after parsing',
|
||||
() => { expect(Validators.max(2)(new FormControl('aaa'))).toBeNull(); });
|
||||
|
||||
it('should return a validation error on big values', () => {
|
||||
expect(Validators.max(2)(new FormControl(3))).toEqual({'max': {'max': 2, 'actual': 3}});
|
||||
});
|
||||
|
||||
it('should return a validation error on big values converted from strings', () => {
|
||||
expect(Validators.max(2)(new FormControl('3'))).toEqual({'max': {'max': 2, 'actual': '3'}});
|
||||
});
|
||||
|
||||
it('should not error on small values',
|
||||
() => { expect(Validators.max(2)(new FormControl(1))).toBeNull(); });
|
||||
|
||||
it('should not error on equal values',
|
||||
() => { expect(Validators.max(2)(new FormControl(2))).toBeNull(); });
|
||||
|
||||
it('should not error on equal values when value is string',
|
||||
() => { expect(Validators.max(2)(new FormControl('2'))).toBeNull(); });
|
||||
|
||||
it('should validate as expected when max value is a string', () => {
|
||||
expect(Validators.max('2' as any)(new FormControl(3))).toEqual({
|
||||
'max': {'max': '2', 'actual': 3}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if max value is undefined',
|
||||
() => { expect(Validators.max(undefined as any)(new FormControl(3))).toBeNull(); });
|
||||
|
||||
it('should return null if max value is null',
|
||||
() => { expect(Validators.max(null as any)(new FormControl(3))).toBeNull(); });
|
||||
it('should not error on null', () => {
|
||||
expect(Validators.min(2)(new FormControl(null))).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
describe('required', () => {
|
||||
it('should error on an empty string',
|
||||
() => { expect(Validators.required(new FormControl(''))).toEqual({'required': true}); });
|
||||
|
||||
it('should error on null',
|
||||
() => { expect(Validators.required(new FormControl(null))).toEqual({'required': true}); });
|
||||
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.required(new FormControl(undefined))).toEqual({'required': true});
|
||||
});
|
||||
|
||||
it('should not error on a non-empty string',
|
||||
() => { expect(Validators.required(new FormControl('not empty'))).toBeNull(); });
|
||||
|
||||
it('should accept zero as valid',
|
||||
() => { expect(Validators.required(new FormControl(0))).toBeNull(); });
|
||||
|
||||
it('should error on an empty array',
|
||||
() => expect(Validators.required(new FormControl([]))).toEqual({'required': true}));
|
||||
|
||||
it('should not error on a non-empty array',
|
||||
() => expect(Validators.required(new FormControl([1, 2]))).toBeNull());
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.min(2)(new FormControl(undefined))).toBeNull();
|
||||
});
|
||||
|
||||
describe('requiredTrue', () => {
|
||||
it('should error on false',
|
||||
() => expect(Validators.requiredTrue(new FormControl(false))).toEqual({'required': true}));
|
||||
|
||||
it('should not error on true',
|
||||
() => expect(Validators.requiredTrue(new FormControl(true))).toBeNull());
|
||||
it('should return null if NaN after parsing', () => {
|
||||
expect(Validators.min(2)(new FormControl('a'))).toBeNull();
|
||||
});
|
||||
|
||||
describe('email', () => {
|
||||
it('should not error on an empty string',
|
||||
() => expect(Validators.email(new FormControl(''))).toBeNull());
|
||||
|
||||
it('should not error on null',
|
||||
() => expect(Validators.email(new FormControl(null))).toBeNull());
|
||||
|
||||
it('should error on invalid email',
|
||||
() => expect(Validators.email(new FormControl('some text'))).toEqual({'email': true}));
|
||||
|
||||
it('should not error on valid email',
|
||||
() => expect(Validators.email(new FormControl('test@gmail.com'))).toBeNull());
|
||||
it('should return a validation error on small values', () => {
|
||||
expect(Validators.min(2)(new FormControl(1))).toEqual({'min': {'min': 2, 'actual': 1}});
|
||||
});
|
||||
|
||||
describe('minLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.minLength(2)(new FormControl(''))).toBeNull(); });
|
||||
it('should return a validation error on small values converted from strings', () => {
|
||||
expect(Validators.min(2)(new FormControl('1'))).toEqual({'min': {'min': 2, 'actual': '1'}});
|
||||
});
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.minLength(2)(new FormControl(null))).toBeNull(); });
|
||||
it('should not error on big values', () => {
|
||||
expect(Validators.min(2)(new FormControl(3))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on undefined',
|
||||
() => { expect(Validators.minLength(2)(new FormControl(undefined))).toBeNull(); });
|
||||
it('should not error on equal values', () => {
|
||||
expect(Validators.min(2)(new FormControl(2))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull(); });
|
||||
it('should not error on equal values when value is string', () => {
|
||||
expect(Validators.min(2)(new FormControl('2'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on short strings', () => {
|
||||
expect(Validators.minLength(2)(new FormControl('a'))).toEqual({
|
||||
'minlength': {'requiredLength': 2, 'actualLength': 1}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error when FormArray has valid length', () => {
|
||||
const fa = new FormArray([new FormControl(''), new FormControl('')]);
|
||||
expect(Validators.minLength(2)(fa)).toBeNull();
|
||||
});
|
||||
|
||||
it('should error when FormArray has invalid length', () => {
|
||||
const fa = new FormArray([new FormControl('')]);
|
||||
expect(Validators.minLength(2)(fa)).toEqual({
|
||||
'minlength': {'requiredLength': 2, 'actualLength': 1}
|
||||
});
|
||||
it('should validate as expected when min value is a string', () => {
|
||||
expect(Validators.min('2' as any)(new FormControl(1))).toEqual({
|
||||
'min': {'min': '2', 'actual': 1}
|
||||
});
|
||||
});
|
||||
|
||||
describe('maxLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.maxLength(2)(new FormControl(''))).toBeNull(); });
|
||||
it('should return null if min value is undefined', () => {
|
||||
expect(Validators.min(undefined as any)(new FormControl(3))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.maxLength(2)(new FormControl(null))).toBeNull(); });
|
||||
it('should return null if min value is null', () => {
|
||||
expect(Validators.min(null as any)(new FormControl(3))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error on undefined',
|
||||
() => { expect(Validators.maxLength(2)(new FormControl(undefined))).toBeNull(); });
|
||||
describe('max', () => {
|
||||
it('should not error on an empty string', () => {
|
||||
expect(Validators.max(2)(new FormControl(''))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.maxLength(2)(new FormControl('aa'))).toBeNull(); });
|
||||
it('should not error on null', () => {
|
||||
expect(Validators.max(2)(new FormControl(null))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on long strings', () => {
|
||||
expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({
|
||||
'maxlength': {'requiredLength': 2, 'actualLength': 3}
|
||||
});
|
||||
});
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.max(2)(new FormControl(undefined))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error when FormArray has valid length', () => {
|
||||
const fa = new FormArray([new FormControl(''), new FormControl('')]);
|
||||
expect(Validators.maxLength(2)(fa)).toBeNull();
|
||||
});
|
||||
it('should return null if NaN after parsing', () => {
|
||||
expect(Validators.max(2)(new FormControl('aaa'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error when FormArray has invalid length', () => {
|
||||
const fa = new FormArray([new FormControl(''), new FormControl('')]);
|
||||
expect(Validators.maxLength(1)(fa)).toEqual({
|
||||
'maxlength': {'requiredLength': 1, 'actualLength': 2}
|
||||
});
|
||||
it('should return a validation error on big values', () => {
|
||||
expect(Validators.max(2)(new FormControl(3))).toEqual({'max': {'max': 2, 'actual': 3}});
|
||||
});
|
||||
|
||||
it('should return a validation error on big values converted from strings', () => {
|
||||
expect(Validators.max(2)(new FormControl('3'))).toEqual({'max': {'max': 2, 'actual': '3'}});
|
||||
});
|
||||
|
||||
it('should not error on small values', () => {
|
||||
expect(Validators.max(2)(new FormControl(1))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on equal values', () => {
|
||||
expect(Validators.max(2)(new FormControl(2))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on equal values when value is string', () => {
|
||||
expect(Validators.max(2)(new FormControl('2'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should validate as expected when max value is a string', () => {
|
||||
expect(Validators.max('2' as any)(new FormControl(3))).toEqual({
|
||||
'max': {'max': '2', 'actual': 3}
|
||||
});
|
||||
});
|
||||
|
||||
describe('pattern', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(''))).toBeNull(); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull(); });
|
||||
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(undefined))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on null value and "null" pattern',
|
||||
() => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should error on failure to match string', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
|
||||
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept RegExp object', () => {
|
||||
const pattern: RegExp = new RegExp('[a-zA-Z ]+');
|
||||
expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on failure to match RegExp object', () => {
|
||||
const pattern: RegExp = new RegExp('^[a-zA-Z ]*$');
|
||||
expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({
|
||||
'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error on "null" pattern',
|
||||
() => expect(Validators.pattern(null !)(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should not error on "undefined" pattern',
|
||||
() => expect(Validators.pattern(undefined !)(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string containing both boundary symbols',
|
||||
() => expect(Validators.pattern('^[aA]*$')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string containing only start boundary symbols',
|
||||
() => expect(Validators.pattern('^[aA]*')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string containing only end boundary symbols',
|
||||
() => expect(Validators.pattern('[aA]*$')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string not containing any boundary symbols',
|
||||
() => expect(Validators.pattern('[aA]*')(new FormControl('aaAA'))).toBeNull());
|
||||
it('should return null if max value is undefined', () => {
|
||||
expect(Validators.max(undefined as any)(new FormControl(3))).toBeNull();
|
||||
});
|
||||
|
||||
describe('compose', () => {
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.compose(null !)).toBe(null); });
|
||||
it('should return null if max value is null', () => {
|
||||
expect(Validators.max(null as any)(new FormControl(3))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('required', () => {
|
||||
it('should error on an empty string', () => {
|
||||
expect(Validators.required(new FormControl(''))).toEqual({'required': true});
|
||||
});
|
||||
|
||||
it('should error on null', () => {
|
||||
expect(Validators.required(new FormControl(null))).toEqual({'required': true});
|
||||
});
|
||||
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.required(new FormControl(undefined))).toEqual({'required': true});
|
||||
});
|
||||
|
||||
it('should not error on a non-empty string', () => {
|
||||
expect(Validators.required(new FormControl('not empty'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should accept zero as valid', () => {
|
||||
expect(Validators.required(new FormControl(0))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on an empty array',
|
||||
() => expect(Validators.required(new FormControl([]))).toEqual({'required': true}));
|
||||
|
||||
it('should not error on a non-empty array',
|
||||
() => expect(Validators.required(new FormControl([1, 2]))).toBeNull());
|
||||
});
|
||||
|
||||
describe('requiredTrue', () => {
|
||||
it('should error on false',
|
||||
() => expect(Validators.requiredTrue(new FormControl(false))).toEqual({'required': true}));
|
||||
|
||||
it('should not error on true',
|
||||
() => expect(Validators.requiredTrue(new FormControl(true))).toBeNull());
|
||||
});
|
||||
|
||||
describe('email', () => {
|
||||
it('should not error on an empty string',
|
||||
() => expect(Validators.email(new FormControl(''))).toBeNull());
|
||||
|
||||
it('should not error on null',
|
||||
() => expect(Validators.email(new FormControl(null))).toBeNull());
|
||||
|
||||
it('should error on invalid email',
|
||||
() => expect(Validators.email(new FormControl('some text'))).toEqual({'email': true}));
|
||||
|
||||
it('should not error on valid email',
|
||||
() => expect(Validators.email(new FormControl('test@gmail.com'))).toBeNull());
|
||||
});
|
||||
|
||||
describe('minLength', () => {
|
||||
it('should not error on an empty string', () => {
|
||||
expect(Validators.minLength(2)(new FormControl(''))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on null', () => {
|
||||
expect(Validators.minLength(2)(new FormControl(null))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.minLength(2)(new FormControl(undefined))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on valid strings', () => {
|
||||
expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on short strings', () => {
|
||||
expect(Validators.minLength(2)(new FormControl('a'))).toEqual({
|
||||
'minlength': {'requiredLength': 2, 'actualLength': 1}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error when FormArray has valid length', () => {
|
||||
const fa = new FormArray([new FormControl(''), new FormControl('')]);
|
||||
expect(Validators.minLength(2)(fa)).toBeNull();
|
||||
});
|
||||
|
||||
it('should error when FormArray has invalid length', () => {
|
||||
const fa = new FormArray([new FormControl('')]);
|
||||
expect(Validators.minLength(2)(fa)).toEqual({
|
||||
'minlength': {'requiredLength': 2, 'actualLength': 1}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('maxLength', () => {
|
||||
it('should not error on an empty string', () => {
|
||||
expect(Validators.maxLength(2)(new FormControl(''))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on null', () => {
|
||||
expect(Validators.maxLength(2)(new FormControl(null))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.maxLength(2)(new FormControl(undefined))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on valid strings', () => {
|
||||
expect(Validators.maxLength(2)(new FormControl('aa'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on long strings', () => {
|
||||
expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({
|
||||
'maxlength': {'requiredLength': 2, 'actualLength': 3}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error when FormArray has valid length', () => {
|
||||
const fa = new FormArray([new FormControl(''), new FormControl('')]);
|
||||
expect(Validators.maxLength(2)(fa)).toBeNull();
|
||||
});
|
||||
|
||||
it('should error when FormArray has invalid length', () => {
|
||||
const fa = new FormArray([new FormControl(''), new FormControl('')]);
|
||||
expect(Validators.maxLength(1)(fa)).toEqual({
|
||||
'maxlength': {'requiredLength': 1, 'actualLength': 2}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('pattern', () => {
|
||||
it('should not error on an empty string', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(''))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on null', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on undefined', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(undefined))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on null value and "null" pattern', () => {
|
||||
expect(Validators.pattern('null')(new FormControl(null))).toBeNull();
|
||||
});
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should error on failure to match string', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
|
||||
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept RegExp object', () => {
|
||||
const pattern: RegExp = new RegExp('[a-zA-Z ]+');
|
||||
expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should error on failure to match RegExp object', () => {
|
||||
const pattern: RegExp = new RegExp('^[a-zA-Z ]*$');
|
||||
expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({
|
||||
'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error on "null" pattern',
|
||||
() => expect(Validators.pattern(null!)(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should not error on "undefined" pattern',
|
||||
() => expect(Validators.pattern(undefined!)(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string containing both boundary symbols',
|
||||
() => expect(Validators.pattern('^[aA]*$')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string containing only start boundary symbols',
|
||||
() => expect(Validators.pattern('^[aA]*')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string containing only end boundary symbols',
|
||||
() => expect(Validators.pattern('[aA]*$')(new FormControl('aaAA'))).toBeNull());
|
||||
|
||||
it('should work with pattern string not containing any boundary symbols',
|
||||
() => expect(Validators.pattern('[aA]*')(new FormControl('aaAA'))).toBeNull());
|
||||
});
|
||||
|
||||
describe('compose', () => {
|
||||
it('should return null when given null', () => {
|
||||
expect(Validators.compose(null!)).toBe(null);
|
||||
});
|
||||
|
||||
it('should collect errors from all the validators', () => {
|
||||
const c = Validators.compose([validator('a', true), validator('b', true)])!;
|
||||
expect(c(new FormControl(''))).toEqual({'a': true, 'b': true});
|
||||
});
|
||||
|
||||
it('should run validators left to right', () => {
|
||||
const c = Validators.compose([validator('a', 1), validator('a', 2)])!;
|
||||
expect(c(new FormControl(''))).toEqual({'a': 2});
|
||||
});
|
||||
|
||||
it('should return null when no errors', () => {
|
||||
const c = Validators.compose([Validators.nullValidator, Validators.nullValidator])!;
|
||||
expect(c(new FormControl(''))).toBeNull();
|
||||
});
|
||||
|
||||
it('should ignore nulls', () => {
|
||||
const c = Validators.compose([null!, Validators.required])!;
|
||||
expect(c(new FormControl(''))).toEqual({'required': true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('composeAsync', () => {
|
||||
describe('promises', () => {
|
||||
function promiseValidator(response: {[key: string]: any}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
const res = c.value != 'expected' ? response : null;
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
}
|
||||
|
||||
it('should return null when given null', () => {
|
||||
expect(Validators.composeAsync(null!)).toBeNull();
|
||||
});
|
||||
|
||||
it('should collect errors from all the validators', fakeAsync(() => {
|
||||
const v = Validators.composeAsync(
|
||||
[promiseValidator({'one': true}), promiseValidator({'two': true})])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap!).toEqual({'one': true, 'two': true});
|
||||
}));
|
||||
|
||||
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
|
||||
const v = Validators.composeAsync(
|
||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap!).toEqual({'one': true});
|
||||
}));
|
||||
|
||||
it('should return null when no errors', fakeAsync(() => {
|
||||
const v = Validators.composeAsync([promiseValidator({'one': true})])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('expected')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap).toBeNull();
|
||||
}));
|
||||
|
||||
it('should ignore nulls', fakeAsync(() => {
|
||||
const v = Validators.composeAsync([promiseValidator({'one': true}), null!])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap!).toEqual({'one': true});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('observables', () => {
|
||||
function observableValidator(response: {[key: string]: any}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
const res = c.value != 'expected' ? response : null;
|
||||
return of(res);
|
||||
};
|
||||
}
|
||||
|
||||
it('should return null when given null', () => {
|
||||
expect(Validators.composeAsync(null!)).toBeNull();
|
||||
});
|
||||
|
||||
it('should collect errors from all the validators', () => {
|
||||
const c = Validators.compose([validator('a', true), validator('b', true)]) !;
|
||||
expect(c(new FormControl(''))).toEqual({'a': true, 'b': true});
|
||||
const v = Validators.composeAsync(
|
||||
[observableValidator({'one': true}), observableValidator({'two': true})])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
|
||||
expect(errorMap!).toEqual({'one': true, 'two': true});
|
||||
});
|
||||
|
||||
it('should run validators left to right', () => {
|
||||
const c = Validators.compose([validator('a', 1), validator('a', 2)]) !;
|
||||
expect(c(new FormControl(''))).toEqual({'a': 2});
|
||||
it('should normalize and evaluate async validator-directives correctly', () => {
|
||||
const v = Validators.composeAsync(
|
||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors)!;
|
||||
|
||||
expect(errorMap!).toEqual({'one': true});
|
||||
});
|
||||
|
||||
it('should return null when no errors', () => {
|
||||
const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]) !;
|
||||
expect(c(new FormControl(''))).toBeNull();
|
||||
const v = Validators.composeAsync([observableValidator({'one': true})])!;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('expected')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
|
||||
expect(errorMap).toBeNull();
|
||||
});
|
||||
|
||||
it('should ignore nulls', () => {
|
||||
const c = Validators.compose([null !, Validators.required]) !;
|
||||
expect(c(new FormControl(''))).toEqual({'required': true});
|
||||
});
|
||||
});
|
||||
const v = Validators.composeAsync([observableValidator({'one': true}), null!])!;
|
||||
|
||||
describe('composeAsync', () => {
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
|
||||
describe('promises', () => {
|
||||
function promiseValidator(response: {[key: string]: any}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
const res = c.value != 'expected' ? response : null;
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
}
|
||||
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.composeAsync(null !)).toBeNull(); });
|
||||
|
||||
it('should collect errors from all the validators', fakeAsync(() => {
|
||||
const v = Validators.composeAsync(
|
||||
[promiseValidator({'one': true}), promiseValidator({'two': true})]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap !).toEqual({'one': true, 'two': true});
|
||||
}));
|
||||
|
||||
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
|
||||
const v = Validators.composeAsync([normalizeAsyncValidator(
|
||||
new AsyncValidatorDirective('expected', {'one': true}))]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap !).toEqual({'one': true});
|
||||
}));
|
||||
|
||||
it('should return null when no errors', fakeAsync(() => {
|
||||
const v = Validators.composeAsync([promiseValidator({'one': true})]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('expected')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap).toBeNull();
|
||||
}));
|
||||
|
||||
it('should ignore nulls', fakeAsync(() => {
|
||||
const v = Validators.composeAsync([promiseValidator({'one': true}), null !]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
tick();
|
||||
|
||||
expect(errorMap !).toEqual({'one': true});
|
||||
}));
|
||||
expect(errorMap!).toEqual({'one': true});
|
||||
});
|
||||
|
||||
describe('observables', () => {
|
||||
function observableValidator(response: {[key: string]: any}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
const res = c.value != 'expected' ? response : null;
|
||||
return of (res);
|
||||
};
|
||||
}
|
||||
it('should wait for all validators before setting errors', fakeAsync(() => {
|
||||
function getTimerObs(time: number, errorMap: {[key: string]: any}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
return timer(time).pipe(map(() => errorMap));
|
||||
};
|
||||
}
|
||||
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.composeAsync(null !)).toBeNull(); });
|
||||
const v = Validators.composeAsync(
|
||||
[getTimerObs(100, {one: true}), getTimerObs(200, {two: true})])!;
|
||||
|
||||
it('should collect errors from all the validators', () => {
|
||||
const v = Validators.composeAsync(
|
||||
[observableValidator({'one': true}), observableValidator({'two': true})]) !;
|
||||
let errorMap: {[key: string]: any}|null = undefined!;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any}|null) => errorMap = errors);
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
|
||||
expect(errorMap !).toEqual({'one': true, 'two': true});
|
||||
});
|
||||
|
||||
it('should normalize and evaluate async validator-directives correctly', () => {
|
||||
const v = Validators.composeAsync(
|
||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors) !;
|
||||
|
||||
expect(errorMap !).toEqual({'one': true});
|
||||
});
|
||||
|
||||
it('should return null when no errors', () => {
|
||||
const v = Validators.composeAsync([observableValidator({'one': true})]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('expected')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
|
||||
expect(errorMap).toBeNull();
|
||||
});
|
||||
|
||||
it('should ignore nulls', () => {
|
||||
const v = Validators.composeAsync([observableValidator({'one': true}), null !]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
|
||||
expect(errorMap !).toEqual({'one': true});
|
||||
});
|
||||
|
||||
it('should wait for all validators before setting errors', fakeAsync(() => {
|
||||
function getTimerObs(time: number, errorMap: {[key: string]: any}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => { return timer(time).pipe(map(() => errorMap)); };
|
||||
}
|
||||
|
||||
const v = Validators.composeAsync(
|
||||
[getTimerObs(100, {one: true}), getTimerObs(200, {two: true})]) !;
|
||||
|
||||
let errorMap: {[key: string]: any}|null = undefined !;
|
||||
(v(new FormControl('invalid')) as Observable<ValidationErrors|null>)
|
||||
.pipe(first())
|
||||
.subscribe((errors: {[key: string]: any} | null) => errorMap = errors);
|
||||
|
||||
tick(100);
|
||||
expect(errorMap).not.toBeDefined(
|
||||
`Expected errors not to be set until all validators came back.`);
|
||||
|
||||
tick(100);
|
||||
expect(errorMap !)
|
||||
.toEqual(
|
||||
{one: true, two: true},
|
||||
`Expected errors to merge once all validators resolved.`);
|
||||
}));
|
||||
});
|
||||
tick(100);
|
||||
expect(errorMap).not.toBeDefined(
|
||||
`Expected errors not to be set until all validators came back.`);
|
||||
|
||||
tick(100);
|
||||
expect(errorMap!).toEqual(
|
||||
{one: true, two: true}, `Expected errors to merge once all validators resolved.`);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
@ -7,14 +7,13 @@
|
||||
*/
|
||||
|
||||
import {Component, Directive, EventEmitter, Input, Output, Type, ViewChild} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
import {AbstractControl, ControlValueAccessor, FormControl, FormGroup, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, NgModel, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
|
||||
|
||||
{
|
||||
describe('value accessors', () => {
|
||||
|
||||
function initTest<T>(component: Type<T>, ...directives: Type<any>[]): ComponentFixture<T> {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [component, ...directives], imports: [FormsModule, ReactiveFormsModule]});
|
||||
@ -64,7 +63,11 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
form.valueChanges.subscribe({next: (value) => { throw 'Should not happen'; }});
|
||||
form.valueChanges.subscribe({
|
||||
next: (value) => {
|
||||
throw 'Should not happen';
|
||||
}
|
||||
});
|
||||
input.nativeElement.value = 'updatedValue';
|
||||
|
||||
dispatchEvent(input.nativeElement, 'change');
|
||||
@ -160,9 +163,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
});
|
||||
|
||||
describe('select controls', () => {
|
||||
|
||||
describe('in reactive forms', () => {
|
||||
|
||||
it(`should support primitive values`, () => {
|
||||
if (isNode) return;
|
||||
const fixture = initTest(FormControlNameSelect);
|
||||
@ -197,7 +198,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
|
||||
it('should throw an error if compareWith is not a function', () => {
|
||||
const fixture = initTest(FormControlSelectWithCompareFn);
|
||||
fixture.componentInstance.compareFn = null !;
|
||||
fixture.componentInstance.compareFn = null!;
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(/compareWith must be a function, but received null/);
|
||||
});
|
||||
@ -238,7 +239,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(select.nativeElement.value).toEqual('3: Object');
|
||||
expect(nyOption.nativeElement.selected).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('in template-driven forms', () => {
|
||||
@ -336,7 +336,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
dispatchEvent(select.nativeElement, 'change');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(comp.selectedCity !['name']).toEqual('NYC');
|
||||
expect(comp.selectedCity!['name']).toEqual('NYC');
|
||||
|
||||
select.nativeElement.value = '0: null';
|
||||
dispatchEvent(select.nativeElement, 'change');
|
||||
@ -348,7 +348,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
it('should throw an error when compareWith is not a function', () => {
|
||||
const fixture = initTest(NgModelSelectWithCustomCompareFnForm);
|
||||
const comp = fixture.componentInstance;
|
||||
comp.compareFn = null !;
|
||||
comp.compareFn = null!;
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(/compareWith must be a function, but received null/);
|
||||
});
|
||||
@ -398,16 +398,11 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(select.nativeElement.value).toEqual('3: Object');
|
||||
expect(nyOption.nativeElement.selected).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('select multiple controls', () => {
|
||||
|
||||
describe('in reactive forms', () => {
|
||||
|
||||
it('should support primitive values', () => {
|
||||
if (isNode) return;
|
||||
const fixture = initTest(FormControlSelectMultiple);
|
||||
@ -432,7 +427,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
|
||||
it('should throw an error when compareWith is not a function', () => {
|
||||
const fixture = initTest(FormControlSelectMultipleWithCompareFn);
|
||||
fixture.componentInstance.compareFn = null !;
|
||||
fixture.componentInstance.compareFn = null!;
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(/compareWith must be a function, but received null/);
|
||||
});
|
||||
@ -448,7 +443,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(select.nativeElement.value).toEqual('0: Object');
|
||||
expect(sfOption.nativeElement.selected).toBe(true);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('in template-driven forms', () => {
|
||||
@ -520,7 +514,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
it('should throw an error when compareWith is not a function', () => {
|
||||
const fixture = initTest(NgModelSelectMultipleWithCustomCompareFnForm);
|
||||
const comp = fixture.componentInstance;
|
||||
comp.compareFn = null !;
|
||||
comp.compareFn = null!;
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(/compareWith must be a function, but received null/);
|
||||
});
|
||||
@ -539,13 +533,10 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(select.nativeElement.value).toEqual('0: Object');
|
||||
expect(sfOption.nativeElement.selected).toBe(true);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('should support <type=radio>', () => {
|
||||
|
||||
describe('in reactive forms', () => {
|
||||
|
||||
it('should support basic functionality', () => {
|
||||
const fixture = initTest(FormControlRadioButtons);
|
||||
const form =
|
||||
@ -562,10 +553,10 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.detectChanges();
|
||||
|
||||
// view -> model
|
||||
expect(form.get('food') !.value).toEqual('chicken');
|
||||
expect(form.get('food')!.value).toEqual('chicken');
|
||||
expect(inputs[1].nativeElement.checked).toEqual(false);
|
||||
|
||||
form.get('food') !.setValue('fish');
|
||||
form.get('food')!.setValue('fish');
|
||||
fixture.detectChanges();
|
||||
|
||||
// programmatic change -> view
|
||||
@ -606,16 +597,16 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.componentInstance.form = form;
|
||||
fixture.detectChanges();
|
||||
|
||||
form.get('food') !.setValue(null);
|
||||
form.get('food')!.setValue(null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const inputs = fixture.debugElement.queryAll(By.css('input'));
|
||||
expect(inputs[0].nativeElement.checked).toEqual(false);
|
||||
|
||||
form.get('food') !.setValue('chicken');
|
||||
form.get('food')!.setValue('chicken');
|
||||
fixture.detectChanges();
|
||||
|
||||
form.get('food') !.setValue(undefined);
|
||||
form.get('food')!.setValue(undefined);
|
||||
fixture.detectChanges();
|
||||
expect(inputs[0].nativeElement.checked).toEqual(false);
|
||||
});
|
||||
@ -706,13 +697,12 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.detectChanges();
|
||||
|
||||
// view -> model
|
||||
expect(form.get('food') !.value).toEqual('chicken');
|
||||
expect(form.get('nested.food') !.value).toEqual('fish');
|
||||
expect(form.get('food')!.value).toEqual('chicken');
|
||||
expect(form.get('nested.food')!.value).toEqual('fish');
|
||||
|
||||
expect(inputs[1].nativeElement.checked).toEqual(false);
|
||||
expect(inputs[2].nativeElement.checked).toEqual(false);
|
||||
expect(inputs[3].nativeElement.checked).toEqual(true);
|
||||
|
||||
});
|
||||
|
||||
it('should disable all radio buttons when disable() is called', () => {
|
||||
@ -728,7 +718,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(inputs[2].nativeElement.disabled).toEqual(false);
|
||||
expect(inputs[3].nativeElement.disabled).toEqual(false);
|
||||
|
||||
form.get('food') !.disable();
|
||||
form.get('food')!.disable();
|
||||
expect(inputs[0].nativeElement.disabled).toEqual(true);
|
||||
expect(inputs[1].nativeElement.disabled).toEqual(true);
|
||||
expect(inputs[2].nativeElement.disabled).toEqual(false);
|
||||
@ -780,7 +770,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(inputs[0].nativeElement.checked).toBe(false);
|
||||
expect(inputs[1].nativeElement.checked).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('in template-driven forms', () => {
|
||||
@ -860,7 +849,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
fixture.componentInstance.food = null !;
|
||||
fixture.componentInstance.food = null!;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
@ -872,7 +861,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
fixture.componentInstance.food = undefined !;
|
||||
fixture.componentInstance.food = undefined!;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(inputs[0].nativeElement.checked).toEqual(false);
|
||||
@ -886,7 +875,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
tick();
|
||||
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
form.control.get('food') !.disable();
|
||||
form.control.get('food')!.disable();
|
||||
tick();
|
||||
|
||||
const inputs = fixture.debugElement.queryAll(By.css('input'));
|
||||
@ -911,15 +900,11 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(inputs[2].nativeElement.disabled).toBe(false);
|
||||
expect(inputs[3].nativeElement.disabled).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('should support <type=range>', () => {
|
||||
|
||||
describe('in reactive forms', () => {
|
||||
|
||||
it('with basic use case', () => {
|
||||
const fixture = initTest(FormControlRangeInput);
|
||||
const control = new FormControl(10);
|
||||
@ -968,7 +953,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
expect(input.nativeElement.value).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('in template-driven forms', () => {
|
||||
@ -987,15 +971,12 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
tick();
|
||||
// view -> model
|
||||
fixture.detectChanges();
|
||||
expect(typeof(fixture.componentInstance.val)).toBe('number');
|
||||
expect(typeof (fixture.componentInstance.val)).toBe('number');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('custom value accessors', () => {
|
||||
|
||||
describe('in reactive forms', () => {
|
||||
it('should support basic functionality', () => {
|
||||
const fixture = initTest(WrappedValueForm, WrappedValue);
|
||||
@ -1014,9 +995,9 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
expect(form.value).toEqual({'login': 'bb'});
|
||||
|
||||
// custom validator
|
||||
expect(form.get('login') !.errors).toEqual({'err': true});
|
||||
expect(form.get('login')!.errors).toEqual({'err': true});
|
||||
form.setValue({login: 'expected'});
|
||||
expect(form.get('login') !.errors).toEqual(null);
|
||||
expect(form.get('login')!.errors).toEqual(null);
|
||||
});
|
||||
|
||||
it('should support non builtin input elements that fire a change event without a \'target\' property',
|
||||
@ -1042,7 +1023,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
});
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.form.status).toEqual('DISABLED');
|
||||
expect(fixture.componentInstance.form.get('login') !.status).toEqual('DISABLED');
|
||||
expect(fixture.componentInstance.form.get('login')!.status).toEqual('DISABLED');
|
||||
});
|
||||
|
||||
it('should support custom accessors without setDisabledState - formControlDirective',
|
||||
@ -1061,9 +1042,9 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
fixture.componentInstance.form = new FormGroup({'login': new FormControl('aa')});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.myInput !.control).toBeDefined();
|
||||
expect(fixture.componentInstance.myInput !.control)
|
||||
.toEqual(fixture.componentInstance.myInput !.controlDir.control);
|
||||
expect(fixture.componentInstance.myInput!.control).toBeDefined();
|
||||
expect(fixture.componentInstance.myInput!.control)
|
||||
.toEqual(fixture.componentInstance.myInput!.controlDir.control);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1089,16 +1070,14 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'form-control-comp', template: `<input type="text" [formControl]="control">`})
|
||||
export class FormControlComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1110,13 +1089,13 @@ export class FormControlComp {
|
||||
})
|
||||
export class FormGroupComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
myGroup !: FormGroup;
|
||||
myGroup!: FormGroup;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
event !: Event;
|
||||
event!: Event;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1125,7 +1104,7 @@ export class FormGroupComp {
|
||||
})
|
||||
class FormControlNumberInput {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1167,7 +1146,7 @@ class FormControlSelectNgValue {
|
||||
})
|
||||
class FormControlSelectWithCompareFn {
|
||||
compareFn:
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2
|
||||
cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}];
|
||||
form = new FormGroup({city: new FormControl({id: 1, name: 'SF'})});
|
||||
}
|
||||
@ -1211,7 +1190,7 @@ class FormControlSelectMultipleNgValue {
|
||||
})
|
||||
class FormControlSelectMultipleWithCompareFn {
|
||||
compareFn:
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2
|
||||
cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}];
|
||||
form = new FormGroup({city: new FormControl([{id: 1, name: 'SF'}])});
|
||||
}
|
||||
@ -1254,7 +1233,7 @@ class NgModelSelectWithNullForm {
|
||||
})
|
||||
class NgModelSelectWithCustomCompareFnForm {
|
||||
compareFn:
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2
|
||||
selectedCity: any = {};
|
||||
cities: any[] = [];
|
||||
}
|
||||
@ -1270,7 +1249,7 @@ class NgModelSelectWithCustomCompareFnForm {
|
||||
})
|
||||
class NgModelSelectMultipleWithCustomCompareFnForm {
|
||||
compareFn:
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2
|
||||
(o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2
|
||||
selectedCities: any[] = [];
|
||||
cities: any[] = [];
|
||||
}
|
||||
@ -1285,7 +1264,7 @@ class NgModelSelectMultipleWithCustomCompareFnForm {
|
||||
})
|
||||
class NgModelSelectMultipleForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
selectedCities !: any[];
|
||||
selectedCities!: any[];
|
||||
cities: any[] = [];
|
||||
}
|
||||
|
||||
@ -1295,7 +1274,7 @@ class NgModelSelectMultipleForm {
|
||||
})
|
||||
class FormControlRangeInput {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
control !: FormControl;
|
||||
control!: FormControl;
|
||||
}
|
||||
|
||||
@Component({selector: 'ng-model-range-form', template: '<input type="range" [(ngModel)]="val">'})
|
||||
@ -1317,7 +1296,7 @@ class NgModelRangeForm {
|
||||
})
|
||||
export class FormControlRadioButtons {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
showRadio = new FormControl('yes');
|
||||
}
|
||||
|
||||
@ -1335,9 +1314,9 @@ export class FormControlRadioButtons {
|
||||
})
|
||||
class NgModelRadioForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
food !: string;
|
||||
food!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
drink !: string;
|
||||
drink!: string;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
@ -1351,37 +1330,55 @@ class NgModelRadioForm {
|
||||
class WrappedValue implements ControlValueAccessor {
|
||||
value: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
onChange !: Function;
|
||||
onChange!: Function;
|
||||
|
||||
writeValue(value: any) { this.value = `!${value}!`; }
|
||||
writeValue(value: any) {
|
||||
this.value = `!${value}!`;
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => void) { this.onChange = fn; }
|
||||
registerOnChange(fn: (value: any) => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
registerOnTouched(fn: any) {}
|
||||
|
||||
handleOnInput(value: any) { this.onChange(value.substring(1, value.length - 1)); }
|
||||
handleOnInput(value: any) {
|
||||
this.onChange(value.substring(1, value.length - 1));
|
||||
}
|
||||
|
||||
validate(c: AbstractControl) { return c.value === 'expected' ? null : {'err': true}; }
|
||||
validate(c: AbstractControl) {
|
||||
return c.value === 'expected' ? null : {'err': true};
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'my-input', template: ''})
|
||||
export class MyInput implements ControlValueAccessor {
|
||||
@Output('input') onInput = new EventEmitter();
|
||||
// TODO(issue/24571): remove '!'.
|
||||
value !: string;
|
||||
value!: string;
|
||||
|
||||
control: AbstractControl|null = null;
|
||||
|
||||
constructor(public controlDir: NgControl) { controlDir.valueAccessor = this; }
|
||||
constructor(public controlDir: NgControl) {
|
||||
controlDir.valueAccessor = this;
|
||||
}
|
||||
|
||||
ngOnInit() { this.control = this.controlDir.control; }
|
||||
ngOnInit() {
|
||||
this.control = this.controlDir.control;
|
||||
}
|
||||
|
||||
writeValue(value: any) { this.value = `!${value}!`; }
|
||||
writeValue(value: any) {
|
||||
this.value = `!${value}!`;
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => void) { this.onInput.subscribe({next: fn}); }
|
||||
registerOnChange(fn: (value: any) => void) {
|
||||
this.onInput.subscribe({next: fn});
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any) {}
|
||||
|
||||
dispatchChangeEvent() { this.onInput.emit(this.value.substring(1, this.value.length - 1)); }
|
||||
dispatchChangeEvent() {
|
||||
this.onInput.emit(this.value.substring(1, this.value.length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1393,7 +1390,7 @@ export class MyInput implements ControlValueAccessor {
|
||||
})
|
||||
export class MyInputForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
@ViewChild(MyInput) myInput: MyInput|null = null;
|
||||
}
|
||||
|
||||
@ -1406,7 +1403,7 @@ export class MyInputForm {
|
||||
})
|
||||
class WrappedValueForm {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
form !: FormGroup;
|
||||
form!: FormGroup;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1418,18 +1415,24 @@ class WrappedValueForm {
|
||||
})
|
||||
export class NgModelCustomComp implements ControlValueAccessor {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
model !: string;
|
||||
model!: string;
|
||||
@Input('disabled') isDisabled: boolean = false;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
changeFn !: (value: any) => void;
|
||||
changeFn!: (value: any) => void;
|
||||
|
||||
writeValue(value: any) { this.model = value; }
|
||||
writeValue(value: any) {
|
||||
this.model = value;
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => void) { this.changeFn = fn; }
|
||||
registerOnChange(fn: (value: any) => void) {
|
||||
this.changeFn = fn;
|
||||
}
|
||||
|
||||
registerOnTouched() {}
|
||||
|
||||
setDisabledState(isDisabled: boolean) { this.isDisabled = isDisabled; }
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
this.isDisabled = isDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1442,6 +1445,6 @@ export class NgModelCustomComp implements ControlValueAccessor {
|
||||
})
|
||||
export class NgModelCustomWrapper {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
name !: string;
|
||||
name!: string;
|
||||
isDisabled = false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user