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;