From dfa66b7700d4cea7cc5456d6e46fef530f322d54 Mon Sep 17 00:00:00 2001 From: cexbrayat Date: Thu, 29 Sep 2016 16:10:55 +0200 Subject: [PATCH] feat(forms): add hasError and getError to AbstractControlDirective Allows cleaner expressions in template-driven forms. Before:
Username is required
After:
Username is required
Fixes #7255 --- .../directives/abstract_control_directive.ts | 8 + .../@angular/forms/test/directives_spec.ts | 148 +++++++----------- tools/public_api_guard/forms/index.d.ts | 2 + 3 files changed, 69 insertions(+), 89 deletions(-) diff --git a/modules/@angular/forms/src/directives/abstract_control_directive.ts b/modules/@angular/forms/src/directives/abstract_control_directive.ts index ef3f1765f85d0d..0e883f4d321765 100644 --- a/modules/@angular/forms/src/directives/abstract_control_directive.ts +++ b/modules/@angular/forms/src/directives/abstract_control_directive.ts @@ -57,4 +57,12 @@ export abstract class AbstractControlDirective { reset(value: any = undefined): void { if (isPresent(this.control)) this.control.reset(value); } + + hasError(errorCode: string, path: string[] = null): boolean { + return isPresent(this.control) ? this.control.hasError(errorCode, path) : false; + } + + getError(errorCode: string, path: string[] = null): any { + return isPresent(this.control) ? this.control.getError(errorCode, path) : null; + } } diff --git a/modules/@angular/forms/test/directives_spec.ts b/modules/@angular/forms/test/directives_spec.ts index eb48c3fd696b0a..b8e3e4a495f273 100644 --- a/modules/@angular/forms/test/directives_spec.ts +++ b/modules/@angular/forms/test/directives_spec.ts @@ -9,7 +9,7 @@ import {SimpleChange} from '@angular/core/src/change_detection'; import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; -import {CheckboxControlValueAccessor, ControlValueAccessor, DefaultValueAccessor, FormArray, FormArrayName, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormGroupName, NgControl, NgForm, NgModel, NgModelGroup, SelectControlValueAccessor, SelectMultipleControlValueAccessor, Validator, Validators} from '@angular/forms'; +import {CheckboxControlValueAccessor, ControlValueAccessor, DefaultValueAccessor, FormArray, FormArrayName, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormGroupName, NgControl, NgForm, NgModel, NgModelGroup, SelectControlValueAccessor, SelectMultipleControlValueAccessor, Validator, Validators, AbstractControlDirective, AbstractControl} from '@angular/forms'; import {composeValidators, selectValueAccessor} from '@angular/forms/src/directives/shared'; import {SpyNgControl, SpyValueAccessor} from './spies'; @@ -27,6 +27,13 @@ class CustomValidatorDirective implements Validator { validate(c: FormControl): {[key: string]: any} { return {'custom': true}; } } +class DummyControlDirective extends AbstractControlDirective { + constructor(private _control: AbstractControl) { + super(); + }; + get control(): any { return this._control; } +} + function asyncValidator(expected: any /** TODO #9100 */, timeout = 0) { return (c: any /** TODO #9100 */) => { var resolve: (result: any) => void; @@ -42,6 +49,42 @@ function asyncValidator(expected: any /** TODO #9100 */, timeout = 0) { } export function main() { + describe('Directives that extend AbstractControlDirective', () => { + + let control: FormControl; + let controlDirective: DummyControlDirective; + + beforeEach(() => { + control = new FormControl(); + controlDirective = new DummyControlDirective(control); + }); + + it('should reexport control properties', () => { + expect(controlDirective.control).toBe(control); + expect(controlDirective.value).toBe(control.value); + expect(controlDirective.valid).toBe(control.valid); + expect(controlDirective.invalid).toBe(control.invalid); + expect(controlDirective.pending).toBe(control.pending); + expect(controlDirective.errors).toBe(control.errors); + expect(controlDirective.pristine).toBe(control.pristine); + expect(controlDirective.dirty).toBe(control.dirty); + expect(controlDirective.touched).toBe(control.touched); + expect(controlDirective.untouched).toBe(control.untouched); + expect(controlDirective.statusChanges).toBe(control.statusChanges); + expect(controlDirective.valueChanges).toBe(control.valueChanges); + }); + + it('should reexport control methods', () => { + expect(controlDirective.hasError('required')).toBe(control.hasError('required')); + expect(controlDirective.getError('required')).toBe(control.getError('required')); + + control.setErrors({required: true}); + expect(controlDirective.hasError('required')).toBe(control.hasError('required')); + expect(controlDirective.getError('required')).toBe(control.getError('required')); + }); + + }); + describe('Form Directives', () => { var defaultAccessor: DefaultValueAccessor; @@ -143,19 +186,8 @@ export function main() { loginControlDir.valueAccessor = new DummyControlValueAccessor(); }); - it('should reexport control properties', () => { - expect(form.control).toBe(formModel); - expect(form.value).toBe(formModel.value); - expect(form.valid).toBe(formModel.valid); - expect(form.invalid).toBe(formModel.invalid); - expect(form.pending).toBe(formModel.pending); - expect(form.errors).toBe(formModel.errors); - expect(form.pristine).toBe(formModel.pristine); - expect(form.dirty).toBe(formModel.dirty); - expect(form.touched).toBe(formModel.touched); - expect(form.untouched).toBe(formModel.untouched); - expect(form.statusChanges).toBe(formModel.statusChanges); - expect(form.valueChanges).toBe(formModel.valueChanges); + it('should extend AbstractControlDirective', () => { + expect(form instanceof AbstractControlDirective).toBe(true); }); describe('addControl', () => { @@ -312,21 +344,8 @@ export function main() { loginControlDir.valueAccessor = new DummyControlValueAccessor(); }); - it('should reexport control properties', () => { - expect(form.control).toBe(formModel); - expect(form.value).toBe(formModel.value); - expect(form.valid).toBe(formModel.valid); - expect(form.invalid).toBe(formModel.invalid); - expect(form.pending).toBe(formModel.pending); - expect(form.errors).toBe(formModel.errors); - expect(form.pristine).toBe(formModel.pristine); - expect(form.dirty).toBe(formModel.dirty); - expect(form.touched).toBe(formModel.touched); - expect(form.untouched).toBe(formModel.untouched); - expect(form.statusChanges).toBe(formModel.statusChanges); - expect(form.valueChanges).toBe(formModel.valueChanges); - expect(form.disabled).toBe(formModel.disabled); - expect(form.enabled).toBe(formModel.enabled); + it('should extend AbstractControlDirective', () => { + expect(form instanceof AbstractControlDirective).toBe(true); }); describe('addControl & addFormGroup', () => { @@ -390,21 +409,8 @@ export function main() { controlGroupDir.name = 'group'; }); - it('should reexport control properties', () => { - expect(controlGroupDir.control).toBe(formModel); - expect(controlGroupDir.value).toBe(formModel.value); - expect(controlGroupDir.valid).toBe(formModel.valid); - expect(controlGroupDir.invalid).toBe(formModel.invalid); - expect(controlGroupDir.pending).toBe(formModel.pending); - expect(controlGroupDir.errors).toBe(formModel.errors); - expect(controlGroupDir.pristine).toBe(formModel.pristine); - expect(controlGroupDir.dirty).toBe(formModel.dirty); - expect(controlGroupDir.touched).toBe(formModel.touched); - expect(controlGroupDir.untouched).toBe(formModel.untouched); - expect(controlGroupDir.statusChanges).toBe(formModel.statusChanges); - expect(controlGroupDir.valueChanges).toBe(formModel.valueChanges); - expect(controlGroupDir.disabled).toBe(formModel.disabled); - expect(controlGroupDir.enabled).toBe(formModel.enabled); + it('should extend AbstractControlDirective', () => { + expect(controlGroupDir instanceof AbstractControlDirective).toBe(true); }); }); @@ -420,19 +426,8 @@ export function main() { formArrayDir.name = 'array'; }); - it('should reexport control properties', () => { - expect(formArrayDir.control).toBe(formModel); - expect(formArrayDir.value).toBe(formModel.value); - expect(formArrayDir.valid).toBe(formModel.valid); - expect(formArrayDir.invalid).toBe(formModel.invalid); - expect(formArrayDir.pending).toBe(formModel.pending); - expect(formArrayDir.errors).toBe(formModel.errors); - expect(formArrayDir.pristine).toBe(formModel.pristine); - expect(formArrayDir.dirty).toBe(formModel.dirty); - expect(formArrayDir.touched).toBe(formModel.touched); - expect(formArrayDir.untouched).toBe(formModel.untouched); - expect(formArrayDir.disabled).toBe(formModel.disabled); - expect(formArrayDir.enabled).toBe(formModel.enabled); + it('should extend AbstractControlDirective', () => { + expect(formArrayDir instanceof AbstractControlDirective).toBe(true); }); }); @@ -464,7 +459,9 @@ export function main() { controlDir.form = control; }); - it('should reexport control properties', () => { checkProperties(control); }); + it('should extend AbstractControlDirective', () => { + expect(controlDir instanceof AbstractControlDirective).toBe(true); + }); it('should reexport new control properties', () => { var newControl = new FormControl(null); @@ -493,22 +490,8 @@ export function main() { ngModel.valueAccessor = new DummyControlValueAccessor(); }); - it('should reexport control properties', () => { - var control = ngModel.control; - expect(ngModel.control).toBe(control); - expect(ngModel.value).toBe(control.value); - expect(ngModel.valid).toBe(control.valid); - expect(ngModel.invalid).toBe(control.invalid); - expect(ngModel.pending).toBe(control.pending); - expect(ngModel.errors).toBe(control.errors); - expect(ngModel.pristine).toBe(control.pristine); - expect(ngModel.dirty).toBe(control.dirty); - expect(ngModel.touched).toBe(control.touched); - expect(ngModel.untouched).toBe(control.untouched); - expect(ngModel.statusChanges).toBe(control.statusChanges); - expect(ngModel.valueChanges).toBe(control.valueChanges); - expect(ngModel.disabled).toBe(control.disabled); - expect(ngModel.enabled).toBe(control.enabled); + it('should extend AbstractControlDirective', () => { + expect(ngModel instanceof AbstractControlDirective).toBe(true); }); it('should throw when no value accessor with named control', () => { @@ -594,21 +577,8 @@ export function main() { controlNameDir._control = formModel; }); - it('should reexport control properties', () => { - expect(controlNameDir.control).toBe(formModel); - expect(controlNameDir.value).toBe(formModel.value); - expect(controlNameDir.valid).toBe(formModel.valid); - expect(controlNameDir.invalid).toBe(formModel.invalid); - expect(controlNameDir.pending).toBe(formModel.pending); - expect(controlNameDir.errors).toBe(formModel.errors); - expect(controlNameDir.pristine).toBe(formModel.pristine); - expect(controlNameDir.dirty).toBe(formModel.dirty); - expect(controlNameDir.touched).toBe(formModel.touched); - expect(controlNameDir.untouched).toBe(formModel.untouched); - expect(controlNameDir.statusChanges).toBe(formModel.statusChanges); - expect(controlNameDir.valueChanges).toBe(formModel.valueChanges); - expect(controlNameDir.disabled).toBe(formModel.disabled); - expect(controlNameDir.enabled).toBe(formModel.enabled); + it('should extend AbstractControlDirective', () => { + expect(controlNameDir instanceof AbstractControlDirective).toBe(true); }); }); }); diff --git a/tools/public_api_guard/forms/index.d.ts b/tools/public_api_guard/forms/index.d.ts index ca108f8610469d..7d4aecb4522921 100644 --- a/tools/public_api_guard/forms/index.d.ts +++ b/tools/public_api_guard/forms/index.d.ts @@ -84,6 +84,8 @@ export declare abstract class AbstractControlDirective { valid: boolean; value: any; valueChanges: Observable; + getError(errorCode: string, path?: string[]): any; + hasError(errorCode: string, path?: string[]): boolean; reset(value?: any): void; }