Skip to content

Commit 12e4747

Browse files
committed
fix(material/slider): handle ngModel initial null value (#27149)
* fix(material/slider): handle ngModel initial null value * fixes yaqs/6342072129453817856 * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value * fixup! fix(material/slider): handle ngModel initial null value (cherry picked from commit 43c6fe3)
1 parent 023c053 commit 12e4747

File tree

3 files changed

+55
-7
lines changed

3 files changed

+55
-7
lines changed

src/material/slider/slider-input.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,21 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
243243
_skipUIUpdate: boolean = false;
244244

245245
/** Callback called when the slider input value changes. */
246-
private _onChangeFn: (value: any) => void = () => {};
246+
protected _onChangeFn: ((value: any) => void) | undefined;
247247

248248
/** Callback called when the slider input has been touched. */
249249
private _onTouchedFn: () => void = () => {};
250250

251+
/**
252+
* Whether the NgModel has been initialized.
253+
*
254+
* This flag is used to ignore ghost null calls to
255+
* writeValue which can break slider initialization.
256+
*
257+
* See https://github.com/angular/angular/issues/14988.
258+
*/
259+
protected _isControlInitialized = false;
260+
251261
constructor(
252262
readonly _ngZone: NgZone,
253263
readonly _elementRef: ElementRef<HTMLInputElement>,
@@ -328,7 +338,7 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
328338
}
329339

330340
_onInput(): void {
331-
this._onChangeFn(this.value);
341+
this._onChangeFn?.(this.value);
332342
// handles arrowing and updating the value when
333343
// a step is defined.
334344
if (this._slider.step || !this._isActive) {
@@ -422,7 +432,7 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
422432

423433
this.value = value;
424434
this.valueChange.emit(this.value);
425-
this._onChangeFn(this.value);
435+
this._onChangeFn?.(this.value);
426436
this._slider._onValueChange(this);
427437
this._slider.step > 0
428438
? this._updateThumbUIByValue()
@@ -500,7 +510,9 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
500510
* @docs-private
501511
*/
502512
writeValue(value: any): void {
503-
this.value = value;
513+
if (this._isControlInitialized || value !== null) {
514+
this.value = value;
515+
}
504516
}
505517

506518
/**
@@ -510,6 +522,7 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
510522
*/
511523
registerOnChange(fn: any): void {
512524
this._onChangeFn = fn;
525+
this._isControlInitialized = true;
513526
}
514527

515528
/**
@@ -738,8 +751,10 @@ export class MatSliderRangeThumb extends MatSliderThumb implements _MatSliderRan
738751
* @docs-private
739752
*/
740753
override writeValue(value: any): void {
741-
this.value = value;
742-
this._updateWidthInactive();
743-
this._updateSibling();
754+
if (this._isControlInitialized || value !== null) {
755+
this.value = value;
756+
this._updateWidthInactive();
757+
this._updateSibling();
758+
}
744759
}
745760
}

src/material/slider/slider.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,21 @@ describe('MDC-based MatSlider', () => {
11681168
}));
11691169
});
11701170

1171+
describe('range slider w/ NgModel edge case', () => {
1172+
it('should initialize correctly despite NgModel `null` bug', fakeAsync(() => {
1173+
const fixture = createComponent(RangeSliderWithNgModelEdgeCase);
1174+
fixture.detectChanges();
1175+
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
1176+
const slider = sliderDebugElement.componentInstance;
1177+
const startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
1178+
const endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
1179+
flush();
1180+
console.log('result: ', startInput.value);
1181+
checkInput(startInput, {min: -1, max: -0.3, value: -0.7, translateX: 90});
1182+
checkInput(endInput, {min: -0.7, max: 0, value: -0.3, translateX: 210});
1183+
}));
1184+
});
1185+
11711186
describe('slider as a custom form control', () => {
11721187
let fixture: ComponentFixture<SliderWithFormControl>;
11731188
let slider: MatSlider;
@@ -1617,6 +1632,22 @@ class RangeSliderWithNgModel {
16171632
endVal: number | undefined = 100;
16181633
}
16191634

1635+
@Component({
1636+
template: `
1637+
<mat-slider min="-1" max="0" step="0.1">
1638+
<input [(ngModel)]="startValue" matSliderStartThumb />
1639+
<input [(ngModel)]="endValue" matSliderEndThumb />
1640+
</mat-slider>
1641+
1642+
`,
1643+
styles: SLIDER_STYLES,
1644+
})
1645+
class RangeSliderWithNgModelEdgeCase {
1646+
@ViewChild(MatSlider) slider: MatSlider;
1647+
startValue: number = -0.7;
1648+
endValue: number = -0.3;
1649+
}
1650+
16201651
@Component({
16211652
template: `
16221653
<mat-slider>

tools/public_api_guard/material/slider.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
218218
// (undocumented)
219219
_initValue(): void;
220220
_isActive: boolean;
221+
protected _isControlInitialized: boolean;
221222
_isFocused: boolean;
222223
_knobRadius: number;
223224
get max(): number;
@@ -232,6 +233,7 @@ export class MatSliderThumb implements _MatSliderThumb, OnDestroy, ControlValueA
232233
_onBlur(): void;
233234
// (undocumented)
234235
_onChange(): void;
236+
protected _onChangeFn: ((value: any) => void) | undefined;
235237
// (undocumented)
236238
_onFocus(): void;
237239
// (undocumented)

0 commit comments

Comments
 (0)