diff --git a/goldens/size-test.yaml b/goldens/size-test.yaml index b402f11a24dc..b596c9bc5e03 100644 --- a/goldens/size-test.yaml +++ b/goldens/size-test.yaml @@ -1,11 +1,15 @@ cdk/drag-drop/all-directives: 153063 cdk/drag-drop/basic: 150321 material-experimental/mdc-chips/basic: 147613 +material-experimental/mdc-form-field/advanced: 220865 +material-experimental/mdc-form-field/basic: 220028 material/autocomplete/without-optgroup: 208091 material/button-toggle/standalone: 119400 material/chips/basic: 162733 material/datepicker/range-picker/without-form-field: 330101 material/expansion/without-accordion: 130562 +material/form-field/advanced: 186234 +material/form-field/basic: 185008 material/list/nav-list: 130473 material/menu/without-lazy-content: 210751 material/radio/without-group: 121448 diff --git a/integration/size-test/material-experimental/mdc-form-field/BUILD.bazel b/integration/size-test/material-experimental/mdc-form-field/BUILD.bazel new file mode 100644 index 000000000000..960d335c5524 --- /dev/null +++ b/integration/size-test/material-experimental/mdc-form-field/BUILD.bazel @@ -0,0 +1,19 @@ +load("//integration/size-test:index.bzl", "size_test") + +size_test( + name = "basic", + file = "basic.ts", + deps = [ + "//src/material-experimental/mdc-form-field", + "//src/material-experimental/mdc-input", + ], +) + +size_test( + name = "advanced", + file = "advanced.ts", + deps = [ + "//src/material-experimental/mdc-form-field", + "//src/material-experimental/mdc-input", + ], +) diff --git a/integration/size-test/material-experimental/mdc-form-field/advanced.ts b/integration/size-test/material-experimental/mdc-form-field/advanced.ts new file mode 100644 index 000000000000..0c6b2180f49b --- /dev/null +++ b/integration/size-test/material-experimental/mdc-form-field/advanced.ts @@ -0,0 +1,27 @@ +import {Component, NgModule} from '@angular/core'; +import {MatFormFieldModule} from '@angular/material-experimental/mdc-form-field'; +import {MatInputModule} from '@angular/material-experimental/mdc-input'; + +/** + * Advanced component using `MatFormField` and `MatInput` in combination with content + * directives such as `MatError`, `MatHint`, `MatPrefix` or `MatSuffix`. + */ +@Component({ + template: ` + +
+
+ Error + Hint + +
+ `, +}) +export class TestComponent {} + +@NgModule({ + imports: [MatInputModule, MatFormFieldModule], + declarations: [TestComponent], + bootstrap: [TestComponent], +}) +export class AppModule {} diff --git a/integration/size-test/material-experimental/mdc-form-field/basic.ts b/integration/size-test/material-experimental/mdc-form-field/basic.ts new file mode 100644 index 000000000000..24f0b8cad171 --- /dev/null +++ b/integration/size-test/material-experimental/mdc-form-field/basic.ts @@ -0,0 +1,24 @@ +import {Component, NgModule} from '@angular/core'; +import {MatFormFieldModule} from '@angular/material-experimental/mdc-form-field'; +import {MatInputModule} from '@angular/material-experimental/mdc-input'; + +/** + * Basic component using `MatFormField` and `MatInput`. Other parts of the form-field + * module such as `MatError`, `MatHint`, `MatPrefix` or `MatSuffix` are not used + * and should be tree-shaken away. + */ +@Component({ + template: ` + + + + `, +}) +export class TestComponent {} + +@NgModule({ + imports: [MatInputModule, MatFormFieldModule], + declarations: [TestComponent], + bootstrap: [TestComponent], +}) +export class AppModule {} diff --git a/integration/size-test/material/form-field/BUILD.bazel b/integration/size-test/material/form-field/BUILD.bazel new file mode 100644 index 000000000000..4030cd1032b9 --- /dev/null +++ b/integration/size-test/material/form-field/BUILD.bazel @@ -0,0 +1,19 @@ +load("//integration/size-test:index.bzl", "size_test") + +size_test( + name = "basic", + file = "basic.ts", + deps = [ + "//src/material/form-field", + "//src/material/input", + ], +) + +size_test( + name = "advanced", + file = "advanced.ts", + deps = [ + "//src/material/form-field", + "//src/material/input", + ], +) diff --git a/integration/size-test/material/form-field/advanced.ts b/integration/size-test/material/form-field/advanced.ts new file mode 100644 index 000000000000..5efcce71d668 --- /dev/null +++ b/integration/size-test/material/form-field/advanced.ts @@ -0,0 +1,27 @@ +import {Component, NgModule} from '@angular/core'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; + +/** + * Advanced component using `MatFormField` and `MatInput` in combination with content + * directives such as `MatError`, `MatHint`, `MatPrefix` or `MatSuffix`. + */ +@Component({ + template: ` + +
+
+ Error + Hint + +
+ `, +}) +export class TestComponent {} + +@NgModule({ + imports: [MatInputModule, MatFormFieldModule], + declarations: [TestComponent], + bootstrap: [TestComponent], +}) +export class AppModule {} diff --git a/integration/size-test/material/form-field/basic.ts b/integration/size-test/material/form-field/basic.ts new file mode 100644 index 000000000000..2e5b64f7f8e9 --- /dev/null +++ b/integration/size-test/material/form-field/basic.ts @@ -0,0 +1,24 @@ +import {Component, NgModule} from '@angular/core'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; + +/** + * Basic component using `MatFormField` and `MatInput`. Other parts of the form-field + * module such as `MatError`, `MatHint`, `MatPrefix` or `MatSuffix` are not used + * and should be tree-shaken away. + */ +@Component({ + template: ` + + + + `, +}) +export class TestComponent {} + +@NgModule({ + imports: [MatInputModule, MatFormFieldModule], + declarations: [TestComponent], + bootstrap: [TestComponent], +}) +export class AppModule {} diff --git a/src/material-experimental/mdc-form-field/directives/error.ts b/src/material-experimental/mdc-form-field/directives/error.ts index 9ffcbec8d56f..61fdc55adcf5 100644 --- a/src/material-experimental/mdc-form-field/directives/error.ts +++ b/src/material-experimental/mdc-form-field/directives/error.ts @@ -6,10 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Input} from '@angular/core'; +import {Directive, InjectionToken, Input} from '@angular/core'; let nextUniqueId = 0; +/** + * Injection token that can be used to reference instances of `MatError`. It serves as + * alternative token to the actual `MatError` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_ERROR = new InjectionToken('MatError'); + /** Single error message to be shown underneath the form-field. */ @Directive({ selector: 'mat-error', @@ -17,7 +24,8 @@ let nextUniqueId = 0; 'class': 'mat-mdc-form-field-error', 'role': 'alert', '[id]': 'id', - } + }, + providers: [{provide: MAT_ERROR, useExisting: MatError}], }) export class MatError { @Input() id: string = `mat-mdc-error-${nextUniqueId++}`; diff --git a/src/material-experimental/mdc-form-field/directives/hint.ts b/src/material-experimental/mdc-form-field/directives/hint.ts index 1f1682b4c851..d86ed428c285 100644 --- a/src/material-experimental/mdc-form-field/directives/hint.ts +++ b/src/material-experimental/mdc-form-field/directives/hint.ts @@ -19,7 +19,7 @@ let nextUniqueId = 0; '[id]': 'id', // Remove align attribute to prevent it from interfering with layout. '[attr.align]': 'null', - } + }, }) export class MatHint { /** Whether to align the hint label at the start or end of the line. */ diff --git a/src/material-experimental/mdc-form-field/directives/prefix.ts b/src/material-experimental/mdc-form-field/directives/prefix.ts index 8c5ee9da0ca7..bc8af2c2a3a1 100644 --- a/src/material-experimental/mdc-form-field/directives/prefix.ts +++ b/src/material-experimental/mdc-form-field/directives/prefix.ts @@ -6,10 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive} from '@angular/core'; +import {Directive, InjectionToken} from '@angular/core'; + +/** + * Injection token that can be used to reference instances of `MatPrefix`. It serves as + * alternative token to the actual `MatPrefix` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_PREFIX = new InjectionToken('MatPrefix'); /** Prefix to be placed in front of the form field. */ @Directive({ selector: '[matPrefix]', + providers: [{provide: MAT_PREFIX, useExisting: MatPrefix}], }) export class MatPrefix {} diff --git a/src/material-experimental/mdc-form-field/directives/suffix.ts b/src/material-experimental/mdc-form-field/directives/suffix.ts index 9e61eec01cf9..edc35cbf1838 100644 --- a/src/material-experimental/mdc-form-field/directives/suffix.ts +++ b/src/material-experimental/mdc-form-field/directives/suffix.ts @@ -6,10 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive} from '@angular/core'; +import {Directive, InjectionToken} from '@angular/core'; + +/** + * Injection token that can be used to reference instances of `MatSuffix`. It serves as + * alternative token to the actual `MatSuffix` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_SUFFIX = new InjectionToken('MatSuffix'); /** Suffix to be placed at the end of the form field. */ @Directive({ selector: '[matSuffix]', + providers: [{provide: MAT_SUFFIX, useExisting: MatSuffix}], }) export class MatSuffix {} diff --git a/src/material-experimental/mdc-form-field/form-field.ts b/src/material-experimental/mdc-form-field/form-field.ts index 1632bfcd4bea..da0f1a098cba 100644 --- a/src/material-experimental/mdc-form-field/form-field.ts +++ b/src/material-experimental/mdc-form-field/form-field.ts @@ -29,11 +29,7 @@ import { ViewEncapsulation } from '@angular/core'; import {NgControl} from '@angular/forms'; -import { - LabelOptions, - MAT_LABEL_GLOBAL_OPTIONS, - ThemePalette -} from '@angular/material/core'; +import {LabelOptions, MAT_LABEL_GLOBAL_OPTIONS, ThemePalette} from '@angular/material/core'; import { getMatFormFieldDuplicatedHintError, getMatFormFieldMissingControlError, @@ -49,14 +45,14 @@ import { } from '@material/textfield'; import {merge, Subject} from 'rxjs'; import {takeUntil} from 'rxjs/operators'; -import {MatError} from './directives/error'; +import {MAT_ERROR, MatError} from './directives/error'; import {MatFormFieldFloatingLabel} from './directives/floating-label'; import {MatHint} from './directives/hint'; import {MatLabel} from './directives/label'; import {MatFormFieldLineRipple} from './directives/line-ripple'; import {MatFormFieldNotchedOutline} from './directives/notched-outline'; -import {MatPrefix} from './directives/prefix'; -import {MatSuffix} from './directives/suffix'; +import {MAT_PREFIX, MatPrefix} from './directives/prefix'; +import {MAT_SUFFIX, MatSuffix} from './directives/suffix'; /** Type for the available floatLabel values. */ export type FloatLabelType = 'always' | 'auto'; @@ -137,9 +133,12 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck @ContentChild(MatLabel) _labelChildNonStatic: MatLabel|undefined; @ContentChild(MatLabel, {static: true}) _labelChildStatic: MatLabel|undefined; @ContentChild(MatFormFieldControl) _formFieldControl: MatFormFieldControl; - @ContentChildren(MatPrefix, {descendants: true}) _prefixChildren: QueryList; - @ContentChildren(MatSuffix, {descendants: true}) _suffixChildren: QueryList; - @ContentChildren(MatError, {descendants: true}) _errorChildren: QueryList; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(MAT_PREFIX as any, {descendants: true}) _prefixChildren: QueryList; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(MAT_SUFFIX as any, {descendants: true}) _suffixChildren: QueryList; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(MAT_ERROR as any, {descendants: true}) _errorChildren: QueryList; @ContentChildren(MatHint, {descendants: true}) _hintChildren: QueryList; /** Whether the required marker should be hidden. */ diff --git a/src/material/form-field/error.ts b/src/material/form-field/error.ts index 6f5e9494fa12..0741e8c6e629 100644 --- a/src/material/form-field/error.ts +++ b/src/material/form-field/error.ts @@ -6,11 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Input} from '@angular/core'; - +import {Directive, InjectionToken, Input} from '@angular/core'; let nextUniqueId = 0; +/** + * Injection token that can be used to reference instances of `MatError`. It serves as + * alternative token to the actual `MatError` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_ERROR = new InjectionToken('MatError'); /** Single error message to be shown underneath the form field. */ @Directive({ @@ -19,7 +24,8 @@ let nextUniqueId = 0; 'class': 'mat-error', 'role': 'alert', '[attr.id]': 'id', - } + }, + providers: [{provide: MAT_ERROR, useExisting: MatError}], }) export class MatError { @Input() id: string = `mat-error-${nextUniqueId++}`; diff --git a/src/material/form-field/form-field.ts b/src/material/form-field/form-field.ts index d644f29aa3cf..7911edf0064b 100644 --- a/src/material/form-field/form-field.ts +++ b/src/material/form-field/form-field.ts @@ -36,7 +36,7 @@ import { } from '@angular/material/core'; import {fromEvent, merge, Subject} from 'rxjs'; import {startWith, take, takeUntil} from 'rxjs/operators'; -import {MatError} from './error'; +import {MAT_ERROR, MatError} from './error'; import {matFormFieldAnimations} from './form-field-animations'; import {MatFormFieldControl} from './form-field-control'; import { @@ -44,11 +44,11 @@ import { getMatFormFieldMissingControlError, getMatFormFieldPlaceholderConflictError, } from './form-field-errors'; -import {MatHint} from './hint'; +import {_MAT_HINT, MatHint} from './hint'; import {MatLabel} from './label'; import {MatPlaceholder} from './placeholder'; -import {MatPrefix} from './prefix'; -import {MatSuffix} from './suffix'; +import {MAT_PREFIX, MatPrefix} from './prefix'; +import {MAT_SUFFIX, MatSuffix} from './suffix'; import {Platform} from '@angular/cdk/platform'; import {NgControl} from '@angular/forms'; import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; @@ -276,10 +276,15 @@ export class MatFormField extends _MatFormFieldMixinBase } @ContentChild(MatPlaceholder) _placeholderChild: MatPlaceholder; - @ContentChildren(MatError, {descendants: true}) _errorChildren: QueryList; - @ContentChildren(MatHint, {descendants: true}) _hintChildren: QueryList; - @ContentChildren(MatPrefix, {descendants: true}) _prefixChildren: QueryList; - @ContentChildren(MatSuffix, {descendants: true}) _suffixChildren: QueryList; + + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(MAT_ERROR as any, {descendants: true}) _errorChildren: QueryList; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(_MAT_HINT as any, {descendants: true}) _hintChildren: QueryList; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(MAT_PREFIX as any, {descendants: true}) _prefixChildren: QueryList; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. + @ContentChildren(MAT_SUFFIX as any, {descendants: true}) _suffixChildren: QueryList; constructor( public _elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef, diff --git a/src/material/form-field/hint.ts b/src/material/form-field/hint.ts index 6299bc3b7a92..e811e0425747 100644 --- a/src/material/form-field/hint.ts +++ b/src/material/form-field/hint.ts @@ -6,11 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Input} from '@angular/core'; - +import {Directive, InjectionToken, Input} from '@angular/core'; let nextUniqueId = 0; +/** + * Injection token that can be used to reference instances of `MatHint`. It serves as + * alternative token to the actual `MatHint` class which could cause unnecessary + * retention of the class and its directive metadata. + * + * *Note*: This is not part of the public API as the MDC-based form-field will not + * need a lightweight token for `MatHint` and we want to reduce breaking changes. + */ +export const _MAT_HINT = new InjectionToken('MatHint'); /** Hint text to be shown underneath the form field control. */ @Directive({ @@ -21,7 +29,8 @@ let nextUniqueId = 0; '[attr.id]': 'id', // Remove align attribute to prevent it from interfering with layout. '[attr.align]': 'null', - } + }, + providers: [{provide: _MAT_HINT, useExisting: MatHint}], }) export class MatHint { /** Whether to align the hint label at the start or end of the line. */ diff --git a/src/material/form-field/prefix.ts b/src/material/form-field/prefix.ts index 46e3b4cadfa5..bc8af2c2a3a1 100644 --- a/src/material/form-field/prefix.ts +++ b/src/material/form-field/prefix.ts @@ -6,11 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive} from '@angular/core'; +import {Directive, InjectionToken} from '@angular/core'; +/** + * Injection token that can be used to reference instances of `MatPrefix`. It serves as + * alternative token to the actual `MatPrefix` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_PREFIX = new InjectionToken('MatPrefix'); /** Prefix to be placed in front of the form field. */ @Directive({ selector: '[matPrefix]', + providers: [{provide: MAT_PREFIX, useExisting: MatPrefix}], }) export class MatPrefix {} diff --git a/src/material/form-field/suffix.ts b/src/material/form-field/suffix.ts index 39bfeaa493c6..edc35cbf1838 100644 --- a/src/material/form-field/suffix.ts +++ b/src/material/form-field/suffix.ts @@ -6,11 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive} from '@angular/core'; +import {Directive, InjectionToken} from '@angular/core'; +/** + * Injection token that can be used to reference instances of `MatSuffix`. It serves as + * alternative token to the actual `MatSuffix` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_SUFFIX = new InjectionToken('MatSuffix'); /** Suffix to be placed at the end of the form field. */ @Directive({ selector: '[matSuffix]', + providers: [{provide: MAT_SUFFIX, useExisting: MatSuffix}], }) export class MatSuffix {} diff --git a/tools/public_api_guard/material/form-field.d.ts b/tools/public_api_guard/material/form-field.d.ts index fd0f9242faca..cc6bf4280c92 100644 --- a/tools/public_api_guard/material/form-field.d.ts +++ b/tools/public_api_guard/material/form-field.d.ts @@ -1,3 +1,5 @@ +export declare const _MAT_HINT: InjectionToken; + export declare type FloatLabelType = 'always' | 'never' | 'auto'; export declare function getMatFormFieldDuplicatedHintError(align: string): Error; @@ -6,10 +8,16 @@ export declare function getMatFormFieldMissingControlError(): Error; export declare function getMatFormFieldPlaceholderConflictError(): Error; +export declare const MAT_ERROR: InjectionToken; + export declare const MAT_FORM_FIELD: InjectionToken; export declare const MAT_FORM_FIELD_DEFAULT_OPTIONS: InjectionToken; +export declare const MAT_PREFIX: InjectionToken; + +export declare const MAT_SUFFIX: InjectionToken; + export declare class MatError { id: string; static ɵdir: i0.ɵɵDirectiveDefWithMeta;