Skip to content

Commit f8fccef

Browse files
committed
refactor(material/checkbox): remove mixin class usages
Replaces mixin class usages in the checkbox with input transforms.
1 parent 7a5842b commit f8fccef

File tree

5 files changed

+60
-87
lines changed

5 files changed

+60
-87
lines changed

src/dev-app/slider/slider-demo.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {MatButtonToggleModule} from '@angular/material/button-toggle';
1414
import {MatCheckboxModule} from '@angular/material/checkbox';
1515
import {MatDialog, MatDialogModule, MAT_DIALOG_DATA} from '@angular/material/dialog';
1616
import {MatButtonModule} from '@angular/material/button';
17+
import {ThemePalette} from '@angular/material/core';
1718

1819
interface DialogData {
1920
color: string;
@@ -40,7 +41,7 @@ interface DialogData {
4041
export class SliderDemo {
4142
discrete = true;
4243
showTickMarks = true;
43-
colorModel = 'primary';
44+
colorModel: ThemePalette = 'primary';
4445

4546
noop = () => {};
4647
min = '0';

src/material/checkbox/BUILD.bazel

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ ng_module(
2020
),
2121
assets = [":checkbox_scss"] + glob(["**/*.html"]),
2222
deps = [
23-
"//src/cdk/coercion",
2423
"//src/material/core",
2524
"@npm//@angular/animations",
2625
"@npm//@angular/core",

src/material/checkbox/checkbox.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
[disabled]="disabled"
1919
[id]="inputId"
2020
[required]="required"
21-
[tabIndex]="tabIndex"
21+
[tabIndex]="disabled ? -1 : tabIndex"
2222
(blur)="_onBlur()"
2323
(click)="_onInputClick()"
2424
(change)="_onInteractionEvent($event)"/>

src/material/checkbox/checkbox.ts

Lines changed: 33 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {
1010
AfterViewInit,
1111
Attribute,
12+
booleanAttribute,
1213
ChangeDetectionStrategy,
1314
ChangeDetectorRef,
1415
Component,
@@ -18,26 +19,16 @@ import {
1819
Inject,
1920
Input,
2021
NgZone,
22+
numberAttribute,
2123
Optional,
2224
Output,
2325
ViewChild,
2426
ViewEncapsulation,
2527
} from '@angular/core';
2628
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
27-
import {
28-
CanColor,
29-
CanDisable,
30-
CanDisableRipple,
31-
HasTabIndex,
32-
MatRipple,
33-
mixinColor,
34-
mixinDisabled,
35-
mixinDisableRipple,
36-
mixinTabIndex,
37-
} from '@angular/material/core';
29+
import {MatRipple} from '@angular/material/core';
3830
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
3931
import {FocusableOption} from '@angular/cdk/a11y';
40-
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
4132
import {
4233
MAT_CHECKBOX_DEFAULT_OPTIONS,
4334
MAT_CHECKBOX_DEFAULT_OPTIONS_FACTORY,
@@ -79,20 +70,6 @@ let nextUniqueId = 0;
7970
// Default checkbox configuration.
8071
const defaults = MAT_CHECKBOX_DEFAULT_OPTIONS_FACTORY();
8172

82-
// Boilerplate for applying mixins to MatCheckbox.
83-
/** @docs-private */
84-
const _MatCheckboxMixinBase = mixinTabIndex(
85-
mixinColor(
86-
mixinDisableRipple(
87-
mixinDisabled(
88-
class {
89-
constructor(public _elementRef: ElementRef) {}
90-
},
91-
),
92-
),
93-
),
94-
);
95-
9673
@Component({
9774
selector: 'mat-checkbox',
9875
templateUrl: 'checkbox.html',
@@ -108,24 +85,14 @@ const _MatCheckboxMixinBase = mixinTabIndex(
10885
// Add classes that users can use to more easily target disabled or checked checkboxes.
10986
'[class.mat-mdc-checkbox-disabled]': 'disabled',
11087
'[class.mat-mdc-checkbox-checked]': 'checked',
88+
'[class]': 'color ? "mat-" + color : "mat-accent"',
11189
},
11290
providers: [MAT_CHECKBOX_CONTROL_VALUE_ACCESSOR],
113-
inputs: ['disableRipple', 'color', 'tabIndex'],
11491
exportAs: 'matCheckbox',
11592
encapsulation: ViewEncapsulation.None,
11693
changeDetection: ChangeDetectionStrategy.OnPush,
11794
})
118-
export class MatCheckbox
119-
extends _MatCheckboxMixinBase
120-
implements
121-
AfterViewInit,
122-
ControlValueAccessor,
123-
CanColor,
124-
CanDisable,
125-
HasTabIndex,
126-
CanDisableRipple,
127-
FocusableOption
128-
{
95+
export class MatCheckbox implements AfterViewInit, ControlValueAccessor, FocusableOption {
12996
/** Focuses the checkbox. */
13097
focus() {
13198
this._inputElement.nativeElement.focus();
@@ -179,14 +146,7 @@ export class MatCheckbox
179146
}
180147

181148
/** Whether the checkbox is required. */
182-
@Input()
183-
get required(): boolean {
184-
return this._required;
185-
}
186-
set required(value: BooleanInput) {
187-
this._required = coerceBooleanProperty(value);
188-
}
189-
private _required: boolean;
149+
@Input({transform: booleanAttribute}) required: boolean;
190150

191151
/** Whether the label should appear after or before the checkbox. Defaults to 'after' */
192152
@Input() labelPosition: 'before' | 'after' = 'after';
@@ -203,12 +163,24 @@ export class MatCheckbox
203163
/** The value attribute of the native input element */
204164
@Input() value: string;
205165

166+
/** Whether the checkbox has a ripple. */
167+
@Input({transform: booleanAttribute}) disableRipple: boolean;
168+
206169
/** The native `<input type="checkbox">` element */
207170
@ViewChild('input') _inputElement: ElementRef<HTMLInputElement>;
208171

209172
/** The native `<label>` element */
210173
@ViewChild('label') _labelElement: ElementRef<HTMLInputElement>;
211174

175+
/** Tabindex for the checkbox. */
176+
@Input({transform: (value: unknown) => (value == null ? undefined : numberAttribute(value))})
177+
tabIndex: number;
178+
179+
// TODO(crisbeto): this should be a ThemePalette, but some internal apps were abusing
180+
// the lack of type checking previously and assigning random strings.
181+
/** Palette color of the checkbox. */
182+
@Input() color: string | undefined;
183+
212184
/**
213185
* Reference to the MatRipple instance of the checkbox.
214186
* @deprecated Considered an implementation detail. To be removed.
@@ -229,16 +201,15 @@ export class MatCheckbox
229201
private _controlValueAccessorChangeFn: (value: any) => void = () => {};
230202

231203
constructor(
232-
elementRef: ElementRef<HTMLElement>,
204+
public _elementRef: ElementRef<HTMLElement>,
233205
private _changeDetectorRef: ChangeDetectorRef,
234206
private _ngZone: NgZone,
235207
@Attribute('tabindex') tabIndex: string,
236208
@Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string,
237209
@Optional() @Inject(MAT_CHECKBOX_DEFAULT_OPTIONS) private _options?: MatCheckboxDefaultOptions,
238210
) {
239-
super(elementRef);
240211
this._options = this._options || defaults;
241-
this.color = this.defaultColor = this._options.color || defaults.color;
212+
this.color = this._options.color || defaults.color;
242213
this.tabIndex = parseInt(tabIndex) || 0;
243214
this.id = this._uniqueId = `mat-mdc-checkbox-${++nextUniqueId}`;
244215
}
@@ -248,33 +219,26 @@ export class MatCheckbox
248219
}
249220

250221
/** Whether the checkbox is checked. */
251-
@Input()
222+
@Input({transform: booleanAttribute})
252223
get checked(): boolean {
253224
return this._checked;
254225
}
255-
set checked(value: BooleanInput) {
256-
const checked = coerceBooleanProperty(value);
257-
258-
if (checked != this.checked) {
259-
this._checked = checked;
226+
set checked(value: boolean) {
227+
if (value != this.checked) {
228+
this._checked = value;
260229
this._changeDetectorRef.markForCheck();
261230
}
262231
}
263232
private _checked: boolean = false;
264233

265-
/**
266-
* Whether the checkbox is disabled. This fully overrides the implementation provided by
267-
* mixinDisabled, but the mixin is still required because mixinTabIndex requires it.
268-
*/
269-
@Input()
270-
override get disabled(): boolean {
234+
/** Whether the checkbox is disabled. */
235+
@Input({transform: booleanAttribute})
236+
get disabled(): boolean {
271237
return this._disabled;
272238
}
273-
override set disabled(value: BooleanInput) {
274-
const newValue = coerceBooleanProperty(value);
275-
276-
if (newValue !== this.disabled) {
277-
this._disabled = newValue;
239+
set disabled(value: boolean) {
240+
if (value !== this.disabled) {
241+
this._disabled = value;
278242
this._changeDetectorRef.markForCheck();
279243
}
280244
}
@@ -286,13 +250,13 @@ export class MatCheckbox
286250
* checkable items. Note that whenever checkbox is manually clicked, indeterminate is immediately
287251
* set to false.
288252
*/
289-
@Input()
253+
@Input({transform: booleanAttribute})
290254
get indeterminate(): boolean {
291255
return this._indeterminate;
292256
}
293-
set indeterminate(value: BooleanInput) {
257+
set indeterminate(value: boolean) {
294258
const changed = value != this._indeterminate;
295-
this._indeterminate = coerceBooleanProperty(value);
259+
this._indeterminate = value;
296260

297261
if (changed) {
298262
if (this._indeterminate) {

tools/public_api_guard/material/checkbox.md

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,13 @@
44
55
```ts
66

7-
import { _AbstractConstructor } from '@angular/material/core';
87
import { AfterViewInit } from '@angular/core';
9-
import { BooleanInput } from '@angular/cdk/coercion';
10-
import { CanColor } from '@angular/material/core';
11-
import { CanDisable } from '@angular/material/core';
12-
import { CanDisableRipple } from '@angular/material/core';
138
import { ChangeDetectorRef } from '@angular/core';
149
import { CheckboxRequiredValidator } from '@angular/forms';
15-
import { _Constructor } from '@angular/material/core';
1610
import { ControlValueAccessor } from '@angular/forms';
1711
import { ElementRef } from '@angular/core';
1812
import { EventEmitter } from '@angular/core';
1913
import { FocusableOption } from '@angular/cdk/a11y';
20-
import { HasTabIndex } from '@angular/material/core';
2114
import * as i0 from '@angular/core';
2215
import * as i3 from '@angular/material/core';
2316
import { InjectionToken } from '@angular/core';
@@ -39,8 +32,8 @@ export function MAT_CHECKBOX_DEFAULT_OPTIONS_FACTORY(): MatCheckboxDefaultOption
3932
export const MAT_CHECKBOX_REQUIRED_VALIDATOR: Provider;
4033

4134
// @public (undocumented)
42-
export class MatCheckbox extends _MatCheckboxMixinBase implements AfterViewInit, ControlValueAccessor, CanColor, CanDisable, HasTabIndex, CanDisableRipple, FocusableOption {
43-
constructor(elementRef: ElementRef<HTMLElement>, _changeDetectorRef: ChangeDetectorRef, _ngZone: NgZone, tabIndex: string, _animationMode?: string | undefined, _options?: MatCheckboxDefaultOptions | undefined);
35+
export class MatCheckbox implements AfterViewInit, ControlValueAccessor, FocusableOption {
36+
constructor(_elementRef: ElementRef<HTMLElement>, _changeDetectorRef: ChangeDetectorRef, _ngZone: NgZone, tabIndex: string, _animationMode?: string | undefined, _options?: MatCheckboxDefaultOptions | undefined);
4437
protected _animationClasses: {
4538
uncheckedToChecked: string;
4639
uncheckedToIndeterminate: string;
@@ -56,17 +49,21 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements AfterViewInit,
5649
ariaLabelledby: string | null;
5750
readonly change: EventEmitter<MatCheckboxChange>;
5851
get checked(): boolean;
59-
set checked(value: BooleanInput);
52+
set checked(value: boolean);
53+
color: string | undefined;
6054
protected _createChangeEvent(isChecked: boolean): MatCheckboxChange;
6155
get disabled(): boolean;
62-
set disabled(value: BooleanInput);
56+
set disabled(value: boolean);
57+
disableRipple: boolean;
58+
// (undocumented)
59+
_elementRef: ElementRef<HTMLElement>;
6360
focus(): void;
6461
protected _getAnimationTargetElement(): HTMLInputElement;
6562
// (undocumented)
6663
protected _handleInputClick(): void;
6764
id: string;
6865
get indeterminate(): boolean;
69-
set indeterminate(value: BooleanInput);
66+
set indeterminate(value: boolean);
7067
readonly indeterminateChange: EventEmitter<boolean>;
7168
_inputElement: ElementRef<HTMLInputElement>;
7269
get inputId(): string;
@@ -76,6 +73,18 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements AfterViewInit,
7673
labelPosition: 'before' | 'after';
7774
name: string | null;
7875
// (undocumented)
76+
static ngAcceptInputType_checked: unknown;
77+
// (undocumented)
78+
static ngAcceptInputType_disabled: unknown;
79+
// (undocumented)
80+
static ngAcceptInputType_disableRipple: unknown;
81+
// (undocumented)
82+
static ngAcceptInputType_indeterminate: unknown;
83+
// (undocumented)
84+
static ngAcceptInputType_required: unknown;
85+
// (undocumented)
86+
static ngAcceptInputType_tabIndex: unknown;
87+
// (undocumented)
7988
ngAfterViewInit(): void;
8089
// (undocumented)
8190
_onBlur(): void;
@@ -92,18 +101,18 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements AfterViewInit,
92101
registerOnChange(fn: (value: any) => void): void;
93102
// (undocumented)
94103
registerOnTouched(fn: any): void;
95-
get required(): boolean;
96-
set required(value: BooleanInput);
104+
required: boolean;
97105
// @deprecated
98106
ripple: MatRipple;
99107
// (undocumented)
100108
setDisabledState(isDisabled: boolean): void;
109+
tabIndex: number;
101110
toggle(): void;
102111
value: string;
103112
// (undocumented)
104113
writeValue(value: any): void;
105114
// (undocumented)
106-
static ɵcmp: i0.ɵɵComponentDeclaration<MatCheckbox, "mat-checkbox", ["matCheckbox"], { "disableRipple": { "alias": "disableRipple"; "required": false; }; "color": { "alias": "color"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; "ariaDescribedby": { "alias": "aria-describedby"; "required": false; }; "id": { "alias": "id"; "required": false; }; "required": { "alias": "required"; "required": false; }; "labelPosition": { "alias": "labelPosition"; "required": false; }; "name": { "alias": "name"; "required": false; }; "value": { "alias": "value"; "required": false; }; "checked": { "alias": "checked"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "indeterminate": { "alias": "indeterminate"; "required": false; }; }, { "change": "change"; "indeterminateChange": "indeterminateChange"; }, never, ["*"], false, never>;
115+
static ɵcmp: i0.ɵɵComponentDeclaration<MatCheckbox, "mat-checkbox", ["matCheckbox"], { "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; "ariaDescribedby": { "alias": "aria-describedby"; "required": false; }; "id": { "alias": "id"; "required": false; }; "required": { "alias": "required"; "required": false; }; "labelPosition": { "alias": "labelPosition"; "required": false; }; "name": { "alias": "name"; "required": false; }; "value": { "alias": "value"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "color": { "alias": "color"; "required": false; }; "checked": { "alias": "checked"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "indeterminate": { "alias": "indeterminate"; "required": false; }; }, { "change": "change"; "indeterminateChange": "indeterminateChange"; }, never, ["*"], false, never>;
107116
// (undocumented)
108117
static ɵfac: i0.ɵɵFactoryDeclaration<MatCheckbox, [null, null, null, { attribute: "tabindex"; }, { optional: true; }, { optional: true; }]>;
109118
}

0 commit comments

Comments
 (0)