diff --git a/packages/forms/src/directives/ng_control_status.ts b/packages/forms/src/directives/ng_control_status.ts index c8773097299..41aad8646de 100644 --- a/packages/forms/src/directives/ng_control_status.ts +++ b/packages/forms/src/directives/ng_control_status.ts @@ -6,39 +6,39 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Self} from '@angular/core'; +import {Directive, Optional, Self} from '@angular/core'; import {AbstractControlDirective} from './abstract_control_directive'; import {ControlContainer} from './control_container'; import {NgControl} from './ng_control'; export class AbstractControlStatus { - private _cd: AbstractControlDirective; + private _cd: AbstractControlDirective|null; - constructor(cd: AbstractControlDirective) { + constructor(cd: AbstractControlDirective|null) { this._cd = cd; } get ngClassUntouched(): boolean { - return this._cd.control ? this._cd.control.untouched : false; + return this._cd?.control?.untouched ?? false; } get ngClassTouched(): boolean { - return this._cd.control ? this._cd.control.touched : false; + return this._cd?.control?.touched ?? false; } get ngClassPristine(): boolean { - return this._cd.control ? this._cd.control.pristine : false; + return this._cd?.control?.pristine ?? false; } get ngClassDirty(): boolean { - return this._cd.control ? this._cd.control.dirty : false; + return this._cd?.control?.dirty ?? false; } get ngClassValid(): boolean { - return this._cd.control ? this._cd.control.valid : false; + return this._cd?.control?.valid ?? false; } get ngClassInvalid(): boolean { - return this._cd.control ? this._cd.control.invalid : false; + return this._cd?.control?.invalid ?? false; } get ngClassPending(): boolean { - return this._cd.control ? this._cd.control.pending : false; + return this._cd?.control?.pending ?? false; } } @@ -99,7 +99,7 @@ export class NgControlStatus extends AbstractControlStatus { host: ngControlStatusHost }) export class NgControlStatusGroup extends AbstractControlStatus { - constructor(@Self() cd: ControlContainer) { + constructor(@Optional() @Self() cd: ControlContainer) { super(cd); } } diff --git a/packages/forms/test/reactive_integration_spec.ts b/packages/forms/test/reactive_integration_spec.ts index 72088ec4eee..ceb64250600 100644 --- a/packages/forms/test/reactive_integration_spec.ts +++ b/packages/forms/test/reactive_integration_spec.ts @@ -94,6 +94,13 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); return TestBed.createComponent(component); } + function initReactiveFormsTest( + component: Type, ...directives: Type[]): ComponentFixture { + TestBed.configureTestingModule( + {declarations: [component, ...directives], imports: [ReactiveFormsModule]}); + return TestBed.createComponent(component); + } + // Helper method that attaches a spy to a `validate` function on a Validator class. function validatorSpyOn(validatorClass: any) { return spyOn(validatorClass.prototype, 'validate').and.callThrough(); @@ -773,6 +780,49 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); }); describe('setting status classes', () => { + it('should not assign status on standalone
element', () => { + @Component({ + selector: 'form-comp', + template: ` +
+ ` + }) + class FormComp { + } + + const fixture = initReactiveFormsTest(FormComp); + fixture.detectChanges(); + + const form = fixture.debugElement.query(By.css('form')).nativeElement; + // Expect no classes added to the
element since it has no + // reactive directives attached and only ReactiveForms module is used. + expect(sortedClassList(form)).toEqual([]); + }); + + it('should not assign status on standalone element with form control inside', () => { + @Component({ + selector: 'form-comp', + template: ` + + +
+ ` + }) + class FormComp { + control = new FormControl('abc'); + } + const fixture = initReactiveFormsTest(FormComp); + fixture.detectChanges(); + + const form = fixture.debugElement.query(By.css('form')).nativeElement; + // Expect no classes added to the
element since it has no + // reactive directives attached and only ReactiveForms module is used. + expect(sortedClassList(form)).toEqual([]); + + const input = fixture.debugElement.query(By.css('input')).nativeElement; + expect(sortedClassList(input)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']); + }); + it('should work with single fields', () => { const fixture = initTest(FormControlComp); const control = new FormControl('', Validators.required);