Skip to content

Commit 227c490

Browse files
crisbetoandrewseguin
authored andcommitted
fix(checkbox): unknown property warning with Ivy during server-side rendering (#17485)
Ivy's check for whether a property supported is basically `if (propName in element)` which logs a warning if it doesn't match. It seems like Domino hasn't implemented the `indeterminate` property for `input` so Angular ends up logging a warning during server-side rendering. These changes switch to setting the property directly to avoid using a property binding. Also fixes that the current Material checkbox wasn't coercing the `indeterminate` input to a boolean.
1 parent bfa88fd commit 227c490

File tree

5 files changed

+46
-5
lines changed

5 files changed

+46
-5
lines changed

src/material-experimental/mdc-checkbox/checkbox.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
[attr.value]="value"
1212
[checked]="checked"
1313
[disabled]="disabled"
14-
[indeterminate]="indeterminate"
1514
[id]="inputId"
1615
[required]="required"
1716
[tabIndex]="tabIndex"

src/material-experimental/mdc-checkbox/checkbox.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
123123
}
124124
set indeterminate(indeterminate) {
125125
this._indeterminate = coerceBooleanProperty(indeterminate);
126+
this._syncIndeterminate(this._indeterminate);
126127
}
127128
private _indeterminate = false;
128129

@@ -257,6 +258,7 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
257258
}
258259

259260
ngAfterViewInit() {
261+
this._syncIndeterminate(this._indeterminate);
260262
this._checkboxFoundation.init();
261263
}
262264

@@ -370,6 +372,21 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
370372
this._changeDetectorRef.markForCheck();
371373
}
372374

375+
/**
376+
* Syncs the indeterminate value with the checkbox DOM node.
377+
*
378+
* We sync `indeterminate` directly on the DOM node, because in Ivy the check for whether a
379+
* property is supported on an element boils down to `if (propName in element)`. Domino's
380+
* HTMLInputElement doesn't have an `indeterminate` property so Ivy will warn during
381+
* server-side rendering.
382+
*/
383+
private _syncIndeterminate(value: boolean) {
384+
const nativeCheckbox = this._nativeCheckbox;
385+
if (nativeCheckbox) {
386+
nativeCheckbox.nativeElement.indeterminate = value;
387+
}
388+
}
389+
373390
static ngAcceptInputType_checked: boolean | string | null | undefined;
374391
static ngAcceptInputType_indeterminate: boolean | string | null | undefined;
375392
static ngAcceptInputType_disabled: boolean | string | null | undefined;

src/material/checkbox/checkbox.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
[disabled]="disabled"
1111
[attr.name]="name"
1212
[tabIndex]="tabIndex"
13-
[indeterminate]="indeterminate"
1413
[attr.aria-label]="ariaLabel || null"
1514
[attr.aria-labelledby]="ariaLabelledby"
1615
[attr.aria-checked]="_getAriaChecked()"

src/material/checkbox/checkbox.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
Output,
2626
ViewChild,
2727
ViewEncapsulation,
28+
AfterViewInit,
2829
} from '@angular/core';
2930
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
3031
import {
@@ -131,7 +132,7 @@ const _MatCheckboxMixinBase:
131132
changeDetection: ChangeDetectionStrategy.OnPush
132133
})
133134
export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAccessor,
134-
AfterViewChecked, OnDestroy, CanColor, CanDisable, HasTabIndex, CanDisableRipple,
135+
AfterViewInit, AfterViewChecked, OnDestroy, CanColor, CanDisable, HasTabIndex, CanDisableRipple,
135136
FocusableOption {
136137

137138
/**
@@ -235,6 +236,10 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
235236
this._clickAction = this._clickAction || this._options.clickAction;
236237
}
237238

239+
ngAfterViewInit() {
240+
this._syncIndeterminate(this._indeterminate);
241+
}
242+
238243
// TODO: Delete next major revision.
239244
ngAfterViewChecked() {}
240245

@@ -281,7 +286,7 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
281286
get indeterminate(): boolean { return this._indeterminate; }
282287
set indeterminate(value: boolean) {
283288
const changed = value != this._indeterminate;
284-
this._indeterminate = value;
289+
this._indeterminate = coerceBooleanProperty(value);
285290

286291
if (changed) {
287292
if (this._indeterminate) {
@@ -292,6 +297,8 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
292297
}
293298
this.indeterminateChange.emit(this._indeterminate);
294299
}
300+
301+
this._syncIndeterminate(this._indeterminate);
295302
}
296303
private _indeterminate: boolean = false;
297304

@@ -470,7 +477,24 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
470477
return `mat-checkbox-anim-${animSuffix}`;
471478
}
472479

480+
/**
481+
* Syncs the indeterminate value with the checkbox DOM node.
482+
*
483+
* We sync `indeterminate` directly on the DOM node, because in Ivy the check for whether a
484+
* property is supported on an element boils down to `if (propName in element)`. Domino's
485+
* HTMLInputElement doesn't have an `indeterminate` property so Ivy will warn during
486+
* server-side rendering.
487+
*/
488+
private _syncIndeterminate(value: boolean) {
489+
const nativeCheckbox = this._inputElement;
490+
491+
if (nativeCheckbox) {
492+
nativeCheckbox.nativeElement.indeterminate = value;
493+
}
494+
}
495+
473496
static ngAcceptInputType_disabled: boolean | string | null | undefined;
474497
static ngAcceptInputType_required: boolean | string | null | undefined;
475498
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
499+
static ngAcceptInputType_indeterminate: boolean | string | null | undefined;
476500
}

tools/public_api_guard/material/checkbox.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export declare function MAT_CHECKBOX_DEFAULT_OPTIONS_FACTORY(): MatCheckboxDefau
1313

1414
export declare const MAT_CHECKBOX_REQUIRED_VALIDATOR: Provider;
1515

16-
export declare class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAccessor, AfterViewChecked, OnDestroy, CanColor, CanDisable, HasTabIndex, CanDisableRipple, FocusableOption {
16+
export declare class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAccessor, AfterViewInit, AfterViewChecked, OnDestroy, CanColor, CanDisable, HasTabIndex, CanDisableRipple, FocusableOption {
1717
_animationMode?: string | undefined;
1818
_inputElement: ElementRef<HTMLInputElement>;
1919
_onTouched: () => any;
@@ -40,6 +40,7 @@ export declare class MatCheckbox extends _MatCheckboxMixinBase implements Contro
4040
_onLabelTextChange(): void;
4141
focus(origin?: FocusOrigin, options?: FocusOptions): void;
4242
ngAfterViewChecked(): void;
43+
ngAfterViewInit(): void;
4344
ngOnDestroy(): void;
4445
registerOnChange(fn: (value: any) => void): void;
4546
registerOnTouched(fn: any): void;
@@ -48,6 +49,7 @@ export declare class MatCheckbox extends _MatCheckboxMixinBase implements Contro
4849
writeValue(value: any): void;
4950
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
5051
static ngAcceptInputType_disabled: boolean | string | null | undefined;
52+
static ngAcceptInputType_indeterminate: boolean | string | null | undefined;
5153
static ngAcceptInputType_required: boolean | string | null | undefined;
5254
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatCheckbox, "mat-checkbox", ["matCheckbox"], { 'disableRipple': "disableRipple", 'color': "color", 'tabIndex': "tabIndex", 'ariaLabel': "aria-label", 'ariaLabelledby': "aria-labelledby", 'id': "id", 'required': "required", 'labelPosition': "labelPosition", 'name': "name", 'value': "value", 'checked': "checked", 'disabled': "disabled", 'indeterminate': "indeterminate" }, { 'change': "change", 'indeterminateChange': "indeterminateChange" }, never>;
5355
static ɵfac: i0.ɵɵFactoryDef<MatCheckbox>;

0 commit comments

Comments
 (0)