From 310fcb619d3006c3670af0edff4925ab7337cc37 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 4 Jun 2021 15:22:11 +0200 Subject: [PATCH] refactor(material/core): no longer define `stateChanges` in `mixinErrorState` The error state mixin provided in `@angular/material/core` currently defines the `stateChanges` class member as part of the mixin. This is unexpected as consumers might deal with the `stateChanges` member differently. e.g. in some components the `stateChanges` field is intended to show up in the docs, or the JSDoc descripton varies. e.g. the observable could emit whenever form-field state changes, and it should be updated, or it emits always when something changes (e.g. even a component input which is not relevant for the form-field control). In general we want to avoid this member being defined in the mixin as the mixin is rather about the error state, and not defining a subject that can emit whenever "state" changes. BREAKING CHANGE: Previously the `mixinErrorState` mixin function defined a class member for `stateChanges`. This is no longer the case, and consumers need to provide the `stateChanges` class member themselves. --- src/material-experimental/mdc-chips/chip-grid.ts | 15 +++++++++++++-- src/material/chips/chip-list.ts | 13 ++++++++++++- src/material/core/common-behaviors/error-state.ts | 14 +++++--------- src/material/datepicker/date-range-input-parts.ts | 5 ++++- src/material/input/input.ts | 13 ++++++++++++- src/material/select/select.ts | 12 ++++++++++++ tools/public_api_guard/material/core.md | 1 - 7 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/material-experimental/mdc-chips/chip-grid.ts b/src/material-experimental/mdc-chips/chip-grid.ts index 57add5da8653..119d184d49b5 100644 --- a/src/material-experimental/mdc-chips/chip-grid.ts +++ b/src/material-experimental/mdc-chips/chip-grid.ts @@ -43,7 +43,7 @@ import { import {MatFormFieldControl} from '@angular/material-experimental/mdc-form-field'; import {LiveAnnouncer} from '@angular/cdk/a11y'; import {MatChipTextControl} from './chip-text-control'; -import {Observable} from 'rxjs'; +import {Observable, Subject} from 'rxjs'; import {startWith, takeUntil} from 'rxjs/operators'; import {MatChipEvent} from './chip'; import {MatChipRow} from './chip-row'; @@ -64,6 +64,13 @@ export class MatChipGridChange { * @docs-private */ class MatChipGridBase extends MatChipSet { + /** + * Emits whenever the component state changes and should cause the parent + * form-field to update. Implemented as part of `MatFormFieldControl`. + * @docs-private + */ + readonly stateChanges = new Subject(); + constructor( liveAnnouncer: LiveAnnouncer, document: any, @@ -72,7 +79,11 @@ class MatChipGridBase extends MatChipSet { public _defaultErrorStateMatcher: ErrorStateMatcher, public _parentForm: NgForm, public _parentFormGroup: FormGroupDirective, - /** @docs-private */ + /** + * Form control bound to the component. + * Implemented as part of `MatFormFieldControl`. + * @docs-private + */ public ngControl: NgControl, ) { super(liveAnnouncer, document, elementRef, changeDetectorRef); diff --git a/src/material/chips/chip-list.ts b/src/material/chips/chip-list.ts index 11b8741ee05b..21dc2cf5e136 100644 --- a/src/material/chips/chip-list.ts +++ b/src/material/chips/chip-list.ts @@ -46,11 +46,22 @@ import {MatChipTextControl} from './chip-text-control'; /** @docs-private */ const _MatChipListBase = mixinErrorState( class { + /** + * Emits whenever the component state changes and should cause the parent + * form-field to update. Implemented as part of `MatFormFieldControl`. + * @docs-private + */ + readonly stateChanges = new Subject(); + constructor( public _defaultErrorStateMatcher: ErrorStateMatcher, public _parentForm: NgForm, public _parentFormGroup: FormGroupDirective, - /** @docs-private */ + /** + * Form control bound to the component. + * Implemented as part of `MatFormFieldControl`. + * @docs-private + */ public ngControl: NgControl, ) {} }, diff --git a/src/material/core/common-behaviors/error-state.ts b/src/material/core/common-behaviors/error-state.ts index 6d547992bc94..70b3b61bdedc 100644 --- a/src/material/core/common-behaviors/error-state.ts +++ b/src/material/core/common-behaviors/error-state.ts @@ -13,8 +13,6 @@ import {AbstractConstructor, Constructor} from './constructor'; /** @docs-private */ export interface CanUpdateErrorState { - /** Emits whenever the component state changes. */ - readonly stateChanges: Subject; /** Updates the error state based on the provided error state matcher. */ updateErrorState(): void; /** Whether the component is in an error state. */ @@ -31,7 +29,12 @@ export interface HasErrorState { _parentFormGroup: FormGroupDirective; _parentForm: NgForm; _defaultErrorStateMatcher: ErrorStateMatcher; + + // These properties are defined as per the `MatFormFieldControl` interface. Since + // this mixin is commonly used with custom form-field controls, we respect the + // properties (also with the public name they need according to `MatFormFieldControl`). ngControl: NgControl; + stateChanges: Subject; } /** @@ -45,13 +48,6 @@ export function mixinErrorState>( base: T, ): CanUpdateErrorStateCtor & T { return class extends base { - // This class member exists as an interop with `MatFormFieldControl` which expects - // a public `stateChanges` observable to emit whenever the form field should be updated. - // The description is not specifically mentioning the error state, as classes using this - // mixin can/should emit an event in other cases too. - /** Emits whenever the component state changes. */ - readonly stateChanges = new Subject(); - /** Whether the component is in an error state. */ errorState: boolean = false; diff --git a/src/material/datepicker/date-range-input-parts.ts b/src/material/datepicker/date-range-input-parts.ts index 19e5641ac8bd..e9254b0e1b88 100644 --- a/src/material/datepicker/date-range-input-parts.ts +++ b/src/material/datepicker/date-range-input-parts.ts @@ -73,7 +73,10 @@ abstract class MatDateRangeInputPartBase extends MatDatepickerInputBase> implements OnInit, DoCheck { - /** @docs-private */ + /** + * Form control bound to this input part. + * @docs-private + */ ngControl: NgControl; /** @docs-private */ diff --git a/src/material/input/input.ts b/src/material/input/input.ts index cf67fd0bd6ff..2154bd962fcd 100644 --- a/src/material/input/input.ts +++ b/src/material/input/input.ts @@ -48,11 +48,22 @@ let nextUniqueId = 0; /** @docs-private */ const _MatInputBase = mixinErrorState( class { + /** + * Emits whenever the component state changes and should cause the parent + * form-field to update. Implemented as part of `MatFormFieldControl`. + * @docs-private + */ + readonly stateChanges = new Subject(); + constructor( public _defaultErrorStateMatcher: ErrorStateMatcher, public _parentForm: NgForm, public _parentFormGroup: FormGroupDirective, - /** @docs-private */ + /** + * Form control bound to the component. + * Implemented as part of `MatFormFieldControl`. + * @docs-private + */ public ngControl: NgControl, ) {} }, diff --git a/src/material/select/select.ts b/src/material/select/select.ts index ecd5e82b6ab8..cb6c64d0aea6 100644 --- a/src/material/select/select.ts +++ b/src/material/select/select.ts @@ -192,11 +192,23 @@ const _MatSelectMixinBase = mixinDisableRipple( mixinDisabled( mixinErrorState( class { + /** + * Emits whenever the component state changes and should cause the parent + * form-field to update. Implemented as part of `MatFormFieldControl`. + * @docs-private + */ + readonly stateChanges = new Subject(); + constructor( public _elementRef: ElementRef, public _defaultErrorStateMatcher: ErrorStateMatcher, public _parentForm: NgForm, public _parentFormGroup: FormGroupDirective, + /** + * Form control bound to the component. + * Implemented as part of `MatFormFieldControl`. + * @docs-private + */ public ngControl: NgControl, ) {} }, diff --git a/tools/public_api_guard/material/core.md b/tools/public_api_guard/material/core.md index 40f3dbf8ec50..809da0837c26 100644 --- a/tools/public_api_guard/material/core.md +++ b/tools/public_api_guard/material/core.md @@ -78,7 +78,6 @@ export interface CanDisableRipple { export interface CanUpdateErrorState { errorState: boolean; errorStateMatcher: ErrorStateMatcher; - readonly stateChanges: Subject; updateErrorState(): void; }