From 6775ea6bc36750a134df1f58e0c61cd0380f152b Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 23 Aug 2018 20:04:22 +0200 Subject: [PATCH] fix(form-field): outline gap not calculated when appearance is provided through DI Fixes the outline gap not being calculated if the `appearance` is set through the default options, because it doesn't go through the setter and we're not guaranteed for the `MutationObserver` callback to fire at the right time. Fixes #12765. --- src/lib/form-field/form-field.ts | 15 +++++---- src/lib/input/input.spec.ts | 52 +++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/lib/form-field/form-field.ts b/src/lib/form-field/form-field.ts index 7f0a2920967c..77adce9f628c 100644 --- a/src/lib/form-field/form-field.ts +++ b/src/lib/form-field/form-field.ts @@ -144,12 +144,12 @@ export class MatFormField extends _MatFormFieldMixinBase /** The form-field appearance style. */ @Input() - get appearance(): MatFormFieldAppearance { - return this._appearance || this._defaultOptions && this._defaultOptions.appearance || 'legacy'; - } + get appearance(): MatFormFieldAppearance { return this._appearance; } set appearance(value: MatFormFieldAppearance) { const oldValue = this._appearance; - this._appearance = value; + + this._appearance = value || (this._defaults && this._defaults.appearance) || 'legacy'; + if (this._appearance === 'outline' && oldValue !== value) { // @breaking-change 7.0.0 Remove this check and else block once _ngZone is required. if (this._ngZone) { @@ -245,8 +245,8 @@ export class MatFormField extends _MatFormFieldMixinBase private _changeDetectorRef: ChangeDetectorRef, @Optional() @Inject(MAT_LABEL_GLOBAL_OPTIONS) labelOptions: LabelOptions, @Optional() private _dir: Directionality, - @Optional() @Inject(MAT_FORM_FIELD_DEFAULT_OPTIONS) private _defaultOptions: - MatFormFieldDefaultOptions, + @Optional() @Inject(MAT_FORM_FIELD_DEFAULT_OPTIONS) + private _defaults: MatFormFieldDefaultOptions, // @breaking-change 7.0.0 _platform, _ngZone and _animationMode to be made required. private _platform?: Platform, private _ngZone?: NgZone, @@ -256,6 +256,9 @@ export class MatFormField extends _MatFormFieldMixinBase this._labelOptions = labelOptions ? labelOptions : {}; this.floatLabel = this._labelOptions.float || 'auto'; this._animationsEnabled = _animationMode !== 'NoopAnimations'; + + // Set the default through here so we invoke the setter on the first run. + this.appearance = (_defaults && _defaults.appearance) ? _defaults.appearance : 'legacy'; } /** diff --git a/src/lib/input/input.spec.ts b/src/lib/input/input.spec.ts index c947618ebd2f..56abcfb59d2e 100644 --- a/src/lib/input/input.spec.ts +++ b/src/lib/input/input.spec.ts @@ -1,6 +1,11 @@ import {Platform, PlatformModule} from '@angular/cdk/platform'; -import {createFakeEvent, dispatchFakeEvent, wrappedErrorMessage} from '@angular/cdk/testing'; -import {ChangeDetectionStrategy, Component, ViewChild, Type, Provider} from '@angular/core'; +import { + createFakeEvent, + dispatchFakeEvent, + wrappedErrorMessage, + MockNgZone, +} from '@angular/cdk/testing'; +import {ChangeDetectionStrategy, Component, ViewChild, Type, Provider, NgZone} from '@angular/core'; import {ComponentFixture, fakeAsync, flush, TestBed} from '@angular/core/testing'; import { FormControl, @@ -28,10 +33,10 @@ import { } from '@angular/material/form-field'; import {By} from '@angular/platform-browser'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; -import {MatInputModule} from './index'; -import {MatInput} from './input'; import {MatStepperModule} from '@angular/material/stepper'; import {MatTabsModule} from '@angular/material/tabs'; +import {MatInputModule} from './index'; +import {MatInput} from './input'; import {MatTextareaAutosize} from './autosize'; describe('MatInput without forms', () => { @@ -1170,6 +1175,35 @@ describe('MatInput with appearance', () => { expect(parseInt(outlineGap.style.width)).toBeFalsy(); })); + it('should calculate the gaps if the default appearance is provided through DI', fakeAsync(() => { + fixture.destroy(); + TestBed.resetTestingModule(); + + let zone: MockNgZone; + const labelFixture = createComponent(MatInputWithLabel, [ + { + provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, + useValue: {appearance: 'outline'} + }, + { + provide: NgZone, + useFactory: () => zone = new MockNgZone() + } + ]); + + labelFixture.detectChanges(); + zone!.simulateZoneExit(); + flush(); + labelFixture.detectChanges(); + + const wrapperElement = labelFixture.nativeElement; + const outlineStart = wrapperElement.querySelector('.mat-form-field-outline-start'); + const outlineGap = wrapperElement.querySelector('.mat-form-field-outline-gap'); + + expect(parseInt(outlineStart.style.width)).toBeGreaterThan(0); + expect(parseInt(outlineGap.style.width)).toBeGreaterThan(0); + })); + }); describe('MatFormField default options', () => { @@ -1586,6 +1620,16 @@ class MatInputOnPush { }) class MatInputWithReadonlyInput {} +@Component({ + template: ` + + Label + + + ` +}) +class MatInputWithLabel {} + @Component({ template: `