Skip to content

Commit

Permalink
feat(form-field): use injection token for providing form-field (angul…
Browse files Browse the repository at this point in the history
…ar#18777)

Implementers of custom form-field controls often want to inject the
parent `MatFormField` optionally. This currently has negative impact
as the whole `MatFormField` class w/ Angular metadata is brought in
due to the class being used as injector token. This can be avoided by
using a separate thin injection token called `MAT_FORM_FIELD`.

We use this now in `MatSelect`, `MatAutocompleteTrigger` and `MatChipList`. These
don't necessarily require a form-field, so it's a significant bundle size improvement.

Another benefit is that the MDC-based form-field no longer needs to re-provide the
standard `MatFormField` w/ the downside of bringing in a lot of unnecessary code.
  • Loading branch information
devversion authored Mar 12, 2020
1 parent b2e8691 commit 8ec44a1
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 10 deletions.
6 changes: 2 additions & 4 deletions src/material-experimental/mdc-form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
import {
getMatFormFieldDuplicatedHintError,
getMatFormFieldMissingControlError,
MatFormField as NonMdcFormField,
MAT_FORM_FIELD,
matFormFieldAnimations,
MatFormFieldControl,
} from '@angular/material/form-field';
Expand Down Expand Up @@ -123,9 +123,7 @@ const FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM = `translateY(-50%)`;
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
// Temporary workaround that allows us to test the MDC form-field against
// components which inject the non-mdc form-field (e.g. autocomplete).
{provide: NonMdcFormField, useExisting: MatFormField}
{provide: MAT_FORM_FIELD, useExisting: MatFormField},
]
})
export class MatFormField implements AfterViewInit, OnDestroy, AfterContentChecked,
Expand Down
4 changes: 2 additions & 2 deletions src/material/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import {
MatOption,
MatOptionSelectionChange,
} from '@angular/material/core';
import {MatFormField} from '@angular/material/form-field';
import {MAT_FORM_FIELD, MatFormField} from '@angular/material/form-field';
import {defer, fromEvent, merge, Observable, of as observableOf, Subject, Subscription} from 'rxjs';
import {delay, filter, map, switchMap, take, tap} from 'rxjs/operators';

Expand Down Expand Up @@ -217,7 +217,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn
private _changeDetectorRef: ChangeDetectorRef,
@Inject(MAT_AUTOCOMPLETE_SCROLL_STRATEGY) scrollStrategy: any,
@Optional() private _dir: Directionality,
@Optional() @Host() private _formField: MatFormField,
@Optional() @Inject(MAT_FORM_FIELD) @Host() private _formField: MatFormField,
@Optional() @Inject(DOCUMENT) private _document: any,
// @breaking-change 8.0.0 Make `_viewportRuler` required.
private _viewportRuler?: ViewportRuler) {
Expand Down
4 changes: 2 additions & 2 deletions src/material/datepicker/datepicker-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
Validators,
} from '@angular/forms';
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats, ThemePalette} from '@angular/material/core';
import {MatFormField} from '@angular/material/form-field';
import {MAT_FORM_FIELD, MatFormField} from '@angular/material/form-field';
import {MAT_INPUT_VALUE_ACCESSOR} from '@angular/material/input';
import {Subscription} from 'rxjs';
import {MatDatepicker} from './datepicker';
Expand Down Expand Up @@ -243,7 +243,7 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
private _elementRef: ElementRef<HTMLInputElement>,
@Optional() public _dateAdapter: DateAdapter<D>,
@Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats,
@Optional() private _formField: MatFormField) {
@Optional() @Inject(MAT_FORM_FIELD) private _formField: MatFormField) {
if (!this._dateAdapter) {
throw createMissingDateImplError('DateAdapter');
}
Expand Down
9 changes: 9 additions & 0 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ export interface MatFormFieldDefaultOptions {
export const MAT_FORM_FIELD_DEFAULT_OPTIONS =
new InjectionToken<MatFormFieldDefaultOptions>('MAT_FORM_FIELD_DEFAULT_OPTIONS');

/**
* Injection token that can be used to inject an instances of `MatFormField`. It serves
* as alternative token to the actual `MatFormField` class which would cause unnecessary
* retention of the `MatFormField` class and its component metadata.
*/
export const MAT_FORM_FIELD = new InjectionToken<MatFormField>('MatFormField');

/** Container for form controls that applies Material Design styling and behavior. */
@Component({
Expand Down Expand Up @@ -147,6 +153,9 @@ export const MAT_FORM_FIELD_DEFAULT_OPTIONS =
inputs: ['color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{provide: MAT_FORM_FIELD, useExisting: MatFormField},
]
})

export class MatFormField extends _MatFormFieldMixinBase
Expand Down
4 changes: 2 additions & 2 deletions src/material/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ import {
mixinErrorState,
mixinTabIndex,
} from '@angular/material/core';
import {MatFormField, MatFormFieldControl} from '@angular/material/form-field';
import {MAT_FORM_FIELD, MatFormField, MatFormFieldControl} from '@angular/material/form-field';
import {defer, merge, Observable, Subject} from 'rxjs';
import {
distinctUntilChanged,
Expand Down Expand Up @@ -515,7 +515,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
@Optional() private _dir: Directionality,
@Optional() _parentForm: NgForm,
@Optional() _parentFormGroup: FormGroupDirective,
@Optional() private _parentFormField: MatFormField,
@Optional() @Inject(MAT_FORM_FIELD) private _parentFormField: MatFormField,
@Self() @Optional() public ngControl: NgControl,
@Attribute('tabindex') tabIndex: string,
@Inject(MAT_SELECT_SCROLL_STRATEGY) scrollStrategyFactory: any,
Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/material/form-field.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export declare function getMatFormFieldMissingControlError(): Error;

export declare function getMatFormFieldPlaceholderConflictError(): Error;

export declare const MAT_FORM_FIELD: InjectionToken<MatFormField>;

export declare const MAT_FORM_FIELD_DEFAULT_OPTIONS: InjectionToken<MatFormFieldDefaultOptions>;

export declare class MatError {
Expand Down

0 comments on commit 8ec44a1

Please sign in to comment.