Skip to content

Commit c2c818c

Browse files
committed
fix(material/slide-toggle): move required validation into component
Currently required validation is implemented as a separate directive. This is problematic, because with standalone users would have to add two imports. These changes move the required validation into the slide toggle and deprecate the old module.
1 parent ec86cf8 commit c2c818c

File tree

4 files changed

+72
-20
lines changed

4 files changed

+72
-20
lines changed

src/material/slide-toggle/module.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,22 @@
77
*/
88

99
import {NgModule} from '@angular/core';
10-
import {MatCommonModule, MatRippleModule} from '@angular/material/core';
10+
import {MatCommonModule} from '@angular/material/core';
1111
import {MatSlideToggle} from './slide-toggle';
1212
import {MatSlideToggleRequiredValidator} from './slide-toggle-required-validator';
1313

14-
/** This module is used by both original and MDC-based slide-toggle implementations. */
14+
/**
15+
* @deprecated No longer used, `MatSlideToggle` implements required validation directly.
16+
* @breaking-change 19.0.0
17+
*/
1518
@NgModule({
1619
imports: [MatSlideToggleRequiredValidator],
1720
exports: [MatSlideToggleRequiredValidator],
1821
})
1922
export class _MatSlideToggleRequiredValidatorModule {}
2023

2124
@NgModule({
22-
imports: [
23-
_MatSlideToggleRequiredValidatorModule,
24-
MatCommonModule,
25-
MatRippleModule,
26-
MatSlideToggle,
27-
],
28-
exports: [_MatSlideToggleRequiredValidatorModule, MatSlideToggle, MatCommonModule],
25+
imports: [MatSlideToggle, MatCommonModule],
26+
exports: [MatSlideToggle, MatCommonModule],
2927
})
3028
export class MatSlideToggleModule {}

src/material/slide-toggle/slide-toggle-required-validator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import {Directive, forwardRef, Provider} from '@angular/core';
1010
import {CheckboxRequiredValidator, NG_VALIDATORS} from '@angular/forms';
1111

12+
/**
13+
* @deprecated No longer used, `MatCheckbox` implements required validation directly.
14+
* @breaking-change 19.0.0
15+
*/
1216
export const MAT_SLIDE_TOGGLE_REQUIRED_VALIDATOR: Provider = {
1317
provide: NG_VALIDATORS,
1418
useExisting: forwardRef(() => MatSlideToggleRequiredValidator),
@@ -22,6 +26,9 @@ export const MAT_SLIDE_TOGGLE_REQUIRED_VALIDATOR: Provider = {
2226
* where the value is always defined.
2327
*
2428
* Required slide-toggle form controls are valid when checked.
29+
*
30+
* @deprecated No longer used, `MatCheckbox` implements required validation directly.
31+
* @breaking-change 19.0.0
2532
*/
2633
@Directive({
2734
selector: `mat-slide-toggle[required][formControlName],

src/material/slide-toggle/slide-toggle.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,22 @@ import {
1919
Inject,
2020
Input,
2121
numberAttribute,
22+
OnChanges,
2223
OnDestroy,
2324
Optional,
2425
Output,
26+
SimpleChanges,
2527
ViewChild,
2628
ViewEncapsulation,
2729
} from '@angular/core';
28-
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
30+
import {
31+
AbstractControl,
32+
ControlValueAccessor,
33+
NG_VALIDATORS,
34+
NG_VALUE_ACCESSOR,
35+
ValidationErrors,
36+
Validator,
37+
} from '@angular/forms';
2938
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
3039
import {FocusMonitor} from '@angular/cdk/a11y';
3140
import {
@@ -34,7 +43,10 @@ import {
3443
} from './slide-toggle-config';
3544
import {MatRipple} from '@angular/material/core';
3645

37-
/** @docs-private */
46+
/**
47+
* @deprecated Will stop being exported.
48+
* @breaking-change 19.0.0
49+
*/
3850
export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR = {
3951
provide: NG_VALUE_ACCESSOR,
4052
useExisting: forwardRef(() => MatSlideToggle),
@@ -75,13 +87,23 @@ let nextUniqueId = 0;
7587
exportAs: 'matSlideToggle',
7688
encapsulation: ViewEncapsulation.None,
7789
changeDetection: ChangeDetectionStrategy.OnPush,
78-
providers: [MAT_SLIDE_TOGGLE_VALUE_ACCESSOR],
90+
providers: [
91+
MAT_SLIDE_TOGGLE_VALUE_ACCESSOR,
92+
{
93+
provide: NG_VALIDATORS,
94+
useExisting: MatSlideToggle,
95+
multi: true,
96+
},
97+
],
7998
standalone: true,
8099
imports: [MatRipple],
81100
})
82-
export class MatSlideToggle implements OnDestroy, AfterContentInit, ControlValueAccessor {
101+
export class MatSlideToggle
102+
implements OnDestroy, AfterContentInit, OnChanges, ControlValueAccessor, Validator
103+
{
83104
private _onChange = (_: any) => {};
84105
private _onTouched = () => {};
106+
private _validatorOnChange = () => {};
85107

86108
private _uniqueId: string;
87109
private _checked: boolean = false;
@@ -211,6 +233,12 @@ export class MatSlideToggle implements OnDestroy, AfterContentInit, ControlValue
211233
});
212234
}
213235

236+
ngOnChanges(changes: SimpleChanges): void {
237+
if (changes['required']) {
238+
this._validatorOnChange();
239+
}
240+
}
241+
214242
ngOnDestroy() {
215243
this._focusMonitor.stopMonitoring(this._elementRef);
216244
}
@@ -230,6 +258,16 @@ export class MatSlideToggle implements OnDestroy, AfterContentInit, ControlValue
230258
this._onTouched = fn;
231259
}
232260

261+
/** Implemented as a part of Validator. */
262+
validate(control: AbstractControl<boolean>): ValidationErrors | null {
263+
return this.required && control.value !== true ? {'required': true} : null;
264+
}
265+
266+
/** Implemented as a part of Validator. */
267+
registerOnValidatorChange(fn: () => void): void {
268+
this._validatorOnChange = fn;
269+
}
270+
233271
/** Implemented as a part of ControlValueAccessor. */
234272
setDisabledState(isDisabled: boolean): void {
235273
this.disabled = isDisabled;

tools/public_api_guard/material/slide-toggle.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
```ts
66

7+
import { AbstractControl } from '@angular/forms';
78
import { AfterContentInit } from '@angular/core';
89
import { ChangeDetectorRef } from '@angular/core';
910
import { CheckboxRequiredValidator } from '@angular/forms';
@@ -12,28 +13,32 @@ import { ElementRef } from '@angular/core';
1213
import { EventEmitter } from '@angular/core';
1314
import { FocusMonitor } from '@angular/cdk/a11y';
1415
import * as i0 from '@angular/core';
15-
import * as i2 from '@angular/material/core';
16+
import * as i3 from '@angular/material/core';
1617
import { InjectionToken } from '@angular/core';
18+
import { OnChanges } from '@angular/core';
1719
import { OnDestroy } from '@angular/core';
1820
import { Provider } from '@angular/core';
21+
import { SimpleChanges } from '@angular/core';
1922
import { ThemePalette } from '@angular/material/core';
2023
import { Type } from '@angular/core';
24+
import { ValidationErrors } from '@angular/forms';
25+
import { Validator } from '@angular/forms';
2126

2227
// @public
2328
export const MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS: InjectionToken<MatSlideToggleDefaultOptions>;
2429

25-
// @public (undocumented)
30+
// @public @deprecated (undocumented)
2631
export const MAT_SLIDE_TOGGLE_REQUIRED_VALIDATOR: Provider;
2732

28-
// @public
33+
// @public @deprecated (undocumented)
2934
export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR: {
3035
provide: InjectionToken<readonly ControlValueAccessor[]>;
3136
useExisting: Type<any>;
3237
multi: boolean;
3338
};
3439

3540
// @public (undocumented)
36-
export class MatSlideToggle implements OnDestroy, AfterContentInit, ControlValueAccessor {
41+
export class MatSlideToggle implements OnDestroy, AfterContentInit, OnChanges, ControlValueAccessor, Validator {
3742
constructor(_elementRef: ElementRef, _focusMonitor: FocusMonitor, _changeDetectorRef: ChangeDetectorRef, tabIndex: string, defaults: MatSlideToggleDefaultOptions, animationMode?: string);
3843
ariaDescribedby: string;
3944
ariaLabel: string | null;
@@ -78,16 +83,20 @@ export class MatSlideToggle implements OnDestroy, AfterContentInit, ControlValue
7883
// (undocumented)
7984
ngAfterContentInit(): void;
8085
// (undocumented)
86+
ngOnChanges(changes: SimpleChanges): void;
87+
// (undocumented)
8188
ngOnDestroy(): void;
8289
_noopAnimations: boolean;
8390
registerOnChange(fn: any): void;
8491
registerOnTouched(fn: any): void;
92+
registerOnValidatorChange(fn: () => void): void;
8593
required: boolean;
8694
setDisabledState(isDisabled: boolean): void;
8795
_switchElement: ElementRef<HTMLElement>;
8896
tabIndex: number;
8997
toggle(): void;
9098
readonly toggleChange: EventEmitter<void>;
99+
validate(control: AbstractControl<boolean>): ValidationErrors | null;
91100
writeValue(value: any): void;
92101
// (undocumented)
93102
static ɵcmp: i0.ɵɵComponentDeclaration<MatSlideToggle, "mat-slide-toggle", ["matSlideToggle"], { "disabled": { "alias": "disabled"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "color": { "alias": "color"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "name": { "alias": "name"; "required": false; }; "id": { "alias": "id"; "required": false; }; "labelPosition": { "alias": "labelPosition"; "required": false; }; "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; "ariaDescribedby": { "alias": "aria-describedby"; "required": false; }; "required": { "alias": "required"; "required": false; }; "checked": { "alias": "checked"; "required": false; }; "hideIcon": { "alias": "hideIcon"; "required": false; }; }, { "change": "change"; "toggleChange": "toggleChange"; }, never, ["*"], true, never>;
@@ -118,18 +127,18 @@ export class MatSlideToggleModule {
118127
// (undocumented)
119128
static ɵinj: i0.ɵɵInjectorDeclaration<MatSlideToggleModule>;
120129
// (undocumented)
121-
static ɵmod: i0.ɵɵNgModuleDeclaration<MatSlideToggleModule, never, [typeof _MatSlideToggleRequiredValidatorModule, typeof i2.MatCommonModule, typeof i2.MatRippleModule, typeof i3.MatSlideToggle], [typeof _MatSlideToggleRequiredValidatorModule, typeof i3.MatSlideToggle, typeof i2.MatCommonModule]>;
130+
static ɵmod: i0.ɵɵNgModuleDeclaration<MatSlideToggleModule, never, [typeof i2.MatSlideToggle, typeof i3.MatCommonModule], [typeof i2.MatSlideToggle, typeof i3.MatCommonModule]>;
122131
}
123132

124-
// @public
133+
// @public @deprecated
125134
export class MatSlideToggleRequiredValidator extends CheckboxRequiredValidator {
126135
// (undocumented)
127136
static ɵdir: i0.ɵɵDirectiveDeclaration<MatSlideToggleRequiredValidator, "mat-slide-toggle[required][formControlName], mat-slide-toggle[required][formControl], mat-slide-toggle[required][ngModel]", never, {}, {}, never, never, true, never>;
128137
// (undocumented)
129138
static ɵfac: i0.ɵɵFactoryDeclaration<MatSlideToggleRequiredValidator, never>;
130139
}
131140

132-
// @public
141+
// @public @deprecated (undocumented)
133142
export class _MatSlideToggleRequiredValidatorModule {
134143
// (undocumented)
135144
static ɵfac: i0.ɵɵFactoryDeclaration<_MatSlideToggleRequiredValidatorModule, never>;

0 commit comments

Comments
 (0)