diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.html b/src/material-experimental/mdc-slide-toggle/slide-toggle.html
index 3d3f69102758..ec29dc3c9c4b 100644
--- a/src/material-experimental/mdc-slide-toggle/slide-toggle.html
+++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.html
@@ -6,6 +6,8 @@
type="button"
[class.mdc-switch--selected]="checked"
[class.mdc-switch--unselected]="!checked"
+ [class.mdc-switch--checked]="checked"
+ [class.mdc-switch--disabled]="disabled"
[tabIndex]="tabIndex"
[disabled]="disabled"
[attr.id]="buttonId"
@@ -14,7 +16,8 @@
[attr.aria-labelledby]="_getAriaLabelledBy()"
[attr.aria-describedby]="ariaDescribedby"
[attr.aria-required]="required || null"
- (click)="_handleClick($event)"
+ [attr.aria-checked]="checked"
+ (click)="_handleClick()"
#switch>
diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts
index 2506e20db667..f24cb8dc54a9 100644
--- a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts
+++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts
@@ -220,7 +220,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
// We fall back to pointing to the label if a value isn't provided.
expect(buttonElement.getAttribute('aria-labelledby')).toMatch(
- /mat-mdc-slide-toggle-label-\d+/,
+ /mat-mdc-slide-toggle-\d+-label/,
);
}));
diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts
index ee18a8d3c1fb..df8401a5630e 100644
--- a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts
+++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts
@@ -10,40 +10,25 @@ import {
ChangeDetectionStrategy,
Component,
ViewEncapsulation,
- AfterViewInit,
- OnDestroy,
forwardRef,
ViewChild,
ElementRef,
- Input,
- Output,
- EventEmitter,
ChangeDetectorRef,
Attribute,
Inject,
Optional,
} from '@angular/core';
-import {deprecated} from '@material/switch';
-import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
-import {
- BooleanInput,
- coerceBooleanProperty,
- coerceNumberProperty,
- NumberInput,
-} from '@angular/cdk/coercion';
+import {NG_VALUE_ACCESSOR} from '@angular/forms';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
-import {ThemePalette} from '@angular/material-experimental/mdc-core';
import {FocusMonitor} from '@angular/cdk/a11y';
+import {_MatSlideToggleBase} from '@angular/material/slide-toggle';
import {
MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS,
MatSlideToggleDefaultOptions,
} from './slide-toggle-config';
-// Increasing integer for generating unique ids for slide-toggle components.
-let nextUniqueId = 0;
-
/** @docs-private */
-export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR: any = {
+export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MatSlideToggle),
multi: true,
@@ -63,6 +48,7 @@ export class MatSlideToggleChange {
selector: 'mat-slide-toggle',
templateUrl: 'slide-toggle.html',
styleUrls: ['slide-toggle.css'],
+ inputs: ['disabled', 'disableRipple', 'color', 'tabIndex'],
host: {
'class': 'mat-mdc-slide-toggle',
'[id]': 'id',
@@ -71,9 +57,6 @@ export class MatSlideToggleChange {
'[attr.aria-label]': 'null',
'[attr.name]': 'null',
'[attr.aria-labelledby]': 'null',
- '[class.mat-primary]': 'color === "primary"',
- '[class.mat-accent]': 'color !== "primary" && color !== "warn"',
- '[class.mat-warn]': 'color === "warn"',
'[class.mat-mdc-slide-toggle-focused]': '_focused',
'[class.mat-mdc-slide-toggle-checked]': 'checked',
'[class._mat-animation-noopable]': '_noopAnimations',
@@ -83,116 +66,9 @@ export class MatSlideToggleChange {
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [MAT_SLIDE_TOGGLE_VALUE_ACCESSOR],
})
-export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDestroy {
- private _onChange = (_: any) => {};
- private _onTouched = () => {};
-
- private _uniqueId: string = `mat-mdc-slide-toggle-${++nextUniqueId}`;
- private _required: boolean = false;
- private _checked: boolean = false;
- private _foundation: deprecated.MDCSwitchFoundation;
- private _adapter: deprecated.MDCSwitchAdapter = {
- addClass: className => this._switchElement.nativeElement.classList.add(className),
- removeClass: className => this._switchElement.nativeElement.classList.remove(className),
- setNativeControlChecked: checked => (this._checked = checked),
- setNativeControlDisabled: disabled => (this._disabled = disabled),
- setNativeControlAttr: (name, value) => {
- this._switchElement.nativeElement.setAttribute(name, value);
- },
- };
-
- /** Whether the slide toggle is currently focused. */
- _focused: boolean;
-
- /** Whether noop animations are enabled. */
- _noopAnimations: boolean;
-
+export class MatSlideToggle extends _MatSlideToggleBase
{
/** Unique ID for the label element. */
- _labelId = `mat-mdc-slide-toggle-label-${++nextUniqueId}`;
-
- /** The color palette for this slide toggle. */
- @Input() color: ThemePalette;
-
- /** Name value will be applied to the button element if present. */
- @Input() name: string | null = null;
-
- /** A unique id for the slide-toggle button. If none is supplied, it will be auto-generated. */
- @Input() id: string = this._uniqueId;
-
- /** Tabindex for the input element. */
- @Input()
- get tabIndex(): number {
- return this._tabIndex;
- }
- set tabIndex(value: NumberInput) {
- this._tabIndex = coerceNumberProperty(value);
- }
- private _tabIndex: number;
-
- /** Whether the label should appear after or before the slide-toggle. Defaults to 'after'. */
- @Input() labelPosition: 'before' | 'after' = 'after';
-
- /** Used to set the aria-label attribute on the underlying button element. */
- @Input('aria-label') ariaLabel: string | null = null;
-
- /** Used to set the aria-labelledby attribute on the underlying button element. */
- @Input('aria-labelledby') ariaLabelledby: string | null = null;
-
- /** Used to set the aria-describedby attribute on the underlying button element. */
- @Input('aria-describedby') ariaDescribedby: string;
-
- /** Whether the slide-toggle is required. */
- @Input()
- get required(): boolean {
- return this._required;
- }
- set required(value: BooleanInput) {
- this._required = coerceBooleanProperty(value);
- }
-
- /** Whether the slide-toggle element is checked or not. */
- @Input()
- get checked(): boolean {
- return this._checked;
- }
- set checked(value: BooleanInput) {
- this._checked = coerceBooleanProperty(value);
-
- if (this._foundation) {
- this._foundation.setChecked(this._checked);
- }
- }
-
- /** Whether to disable the ripple on this checkbox. */
- @Input()
- get disableRipple(): boolean {
- return this._disableRipple;
- }
- set disableRipple(disableRipple: BooleanInput) {
- this._disableRipple = coerceBooleanProperty(disableRipple);
- }
- private _disableRipple = false;
-
- /** Whether the slide toggle is disabled. */
- @Input()
- get disabled(): boolean {
- return this._disabled;
- }
- set disabled(disabled: BooleanInput) {
- this._disabled = coerceBooleanProperty(disabled);
-
- if (this._foundation) {
- this._foundation.setDisabled(this._disabled);
- }
- }
- private _disabled = false;
-
- /** An event will be dispatched each time the slide-toggle changes its value. */
- @Output() readonly change: EventEmitter =
- new EventEmitter();
-
- /** Event will be dispatched each time the slide-toggle input is toggled. */
- @Output() readonly toggleChange: EventEmitter = new EventEmitter();
+ _labelId: string;
/** Returns the unique id for the visual hidden button. */
get buttonId(): string {
@@ -203,51 +79,29 @@ export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDe
@ViewChild('switch') _switchElement: ElementRef;
constructor(
- private _elementRef: ElementRef,
- private _focusMonitor: FocusMonitor,
- private _changeDetectorRef: ChangeDetectorRef,
+ elementRef: ElementRef,
+ focusMonitor: FocusMonitor,
+ changeDetectorRef: ChangeDetectorRef,
@Attribute('tabindex') tabIndex: string,
@Inject(MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS)
- public defaults: MatSlideToggleDefaultOptions,
+ defaults: MatSlideToggleDefaultOptions,
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,
) {
- this.tabIndex = parseInt(tabIndex) || 0;
- this.color = defaults.color || 'accent';
- this._noopAnimations = animationMode === 'NoopAnimations';
- }
-
- ngAfterViewInit() {
- const foundation = (this._foundation = new deprecated.MDCSwitchFoundation(this._adapter));
- foundation.setDisabled(this.disabled);
- foundation.setChecked(this.checked);
-
- this._focusMonitor.monitor(this._elementRef, true).subscribe(focusOrigin => {
- if (focusOrigin === 'keyboard' || focusOrigin === 'program') {
- this._focused = true;
- } else if (!focusOrigin) {
- // When a focused element becomes disabled, the browser *immediately* fires a blur event.
- // Angular does not expect events to be raised during change detection, so any state
- // change (such as a form control's ng-touched) will cause a changed-after-checked error.
- // See https://github.com/angular/angular/issues/17793. To work around this, we defer
- // telling the form control it has been touched until the next tick.
- Promise.resolve().then(() => {
- this._focused = false;
- this._onTouched();
- this._changeDetectorRef.markForCheck();
- });
- }
- });
- }
-
- ngOnDestroy() {
- this._focusMonitor.stopMonitoring(this._elementRef);
- this._foundation?.destroy();
+ super(
+ elementRef,
+ focusMonitor,
+ changeDetectorRef,
+ tabIndex,
+ defaults,
+ animationMode,
+ 'mat-mdc-slide-toggle-',
+ );
+ this._labelId = this._uniqueId + '-label';
}
/** Method being called whenever the underlying button is clicked. */
- _handleClick(event: Event) {
+ _handleClick() {
this.toggleChange.emit();
- this._foundation.handleChange(event);
if (!this.defaults.disableToggleValue) {
this.checked = !this.checked;
@@ -256,37 +110,13 @@ export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDe
}
}
- /** Implemented as part of ControlValueAccessor. */
- writeValue(value: any): void {
- this.checked = !!value;
- this._changeDetectorRef.markForCheck();
- }
-
- /** Implemented as part of ControlValueAccessor. */
- registerOnChange(fn: any): void {
- this._onChange = fn;
- }
-
- /** Implemented as part of ControlValueAccessor. */
- registerOnTouched(fn: any): void {
- this._onTouched = fn;
- }
-
- /** Implemented as a part of ControlValueAccessor. */
- setDisabledState(isDisabled: boolean): void {
- this.disabled = isDisabled;
- this._changeDetectorRef.markForCheck();
- }
-
/** Focuses the slide-toggle. */
focus(): void {
this._switchElement.nativeElement.focus();
}
- /** Toggles the checked state of the slide-toggle. */
- toggle(): void {
- this.checked = !this.checked;
- this._onChange(this.checked);
+ protected _createChangeEvent(isChecked: boolean) {
+ return new MatSlideToggleChange(this, isChecked);
}
_getAriaLabelledBy() {
diff --git a/src/material/slide-toggle/slide-toggle.html b/src/material/slide-toggle/slide-toggle.html
index c00d1406ea3f..c7038930c12a 100644
--- a/src/material/slide-toggle/slide-toggle.html
+++ b/src/material/slide-toggle/slide-toggle.html
@@ -1,5 +1,5 @@