From 534e6a3a18111e4dfd822a8ad492aa730b67cb14 Mon Sep 17 00:00:00 2001 From: wagnermaciel Date: Tue, 23 Feb 2021 08:26:03 -0800 Subject: [PATCH 1/4] feat(material-experimental/mdc-slider): implement control value accessor --- .../mdc-slider/slider.ts | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/material-experimental/mdc-slider/slider.ts b/src/material-experimental/mdc-slider/slider.ts index 59f4f91aa527..9af3baf389f6 100644 --- a/src/material-experimental/mdc-slider/slider.ts +++ b/src/material-experimental/mdc-slider/slider.ts @@ -23,6 +23,7 @@ import { Directive, ElementRef, EventEmitter, + forwardRef, Inject, Input, NgZone, @@ -33,6 +34,7 @@ import { ViewChildren, ViewEncapsulation, } from '@angular/core'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import { CanColorCtor, MatRipple, @@ -236,6 +238,17 @@ export class MatSliderVisualThumb implements AfterViewInit, OnDestroy { } } +/** + * Provider Expression that allows slider thumb inputs to register as a ControlValueAccessor. + * This allows it to support [(ngModel)] and [formControl]. + * @docs-private + */ +export const MAT_SLIDER_THUMB_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MatSliderThumb), + multi: true +}; + /** * Directive that adds slider-specific behaviors to an input element inside ``. * Up to two may be placed inside of a ``. @@ -249,11 +262,12 @@ export class MatSliderVisualThumb implements AfterViewInit, OnDestroy { host: { 'class': 'mdc-slider__input', 'type': 'range', - '(blur)': '_blur.emit()', + '(blur)': '_onBlur()', '(focus)': '_focus.emit()', }, + providers: [MAT_SLIDER_THUMB_VALUE_ACCESSOR], }) -export class MatSliderThumb implements AfterViewInit { +export class MatSliderThumb implements AfterViewInit, ControlValueAccessor { // ** IMPORTANT NOTE ** // @@ -298,6 +312,18 @@ export class MatSliderThumb implements AfterViewInit { /** Event emitted every time the MatSliderThumb is focused. */ @Output() readonly _focus: EventEmitter = new EventEmitter(); + /** + * A callback function that is called when the + * control's value changes in the UI (ControlValueAccessor). + */ + _onChange: (value: any) => void = () => {}; + + /** + * A callback function that is called by the forms API on + * initialization to update the form model on blur (ControlValueAccessor). + */ + private _onTouched: () => any = () => {}; + /** Indicates which slider thumb this input corresponds to. */ _thumbPosition: Thumb = this._elementRef.nativeElement.hasAttribute('matSliderStartThumb') ? Thumb.START @@ -331,6 +357,37 @@ export class MatSliderThumb implements AfterViewInit { } } + _onBlur(): void { + this._onTouched(); + this._blur.emit(); + } + + /** + * Sets the model value. Implemented as part of ControlValueAccessor. + * @param value + */ + writeValue(value: any): void { + this.value = value; + } + + /** + * Registers a callback to be triggered when the value has changed. + * Implemented as part of ControlValueAccessor. + * @param fn Callback to be registered. + */ + registerOnChange(fn: any): void { + this._onChange = fn; + } + + /** + * Registers a callback to be triggered when the component is touched. + * Implemented as part of ControlValueAccessor. + * @param fn Callback to be registered. + */ + registerOnTouched(fn: any): void { + this._onTouched = fn; + } + /** Returns true if this slider input currently has focus. */ _isFocused(): boolean { return this._document.activeElement === this._hostElement; @@ -709,7 +766,9 @@ class SliderAdapter implements MDCSliderAdapter { } // We ignore emitChangeEvent and emitInputEvent because the slider inputs // are already exposed so users can just listen for those events directly themselves. - emitChangeEvent = (value: number, thumbPosition: Thumb): void => {}; + emitChangeEvent = (value: number, thumbPosition: Thumb): void => { + this._delegate._getInput(thumbPosition)._onChange(value); + } emitInputEvent = (value: number, thumbPosition: Thumb): void => {}; emitDragStartEvent = (value: number, thumbPosition: Thumb): void => { const input = this._delegate._getInput(thumbPosition); From 0838146b38bcaaa557f0707d5a5418039359d136 Mon Sep 17 00:00:00 2001 From: wagnermaciel Date: Thu, 25 Feb 2021 09:47:31 -0800 Subject: [PATCH 2/4] fix(material-experimental/mdc-slider): code review changes * put NG_VALUE_ACCESSOR provider definition inline * fix _onTouched type definition * implement setDisabledState --- .../mdc-slider/slider.ts | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/material-experimental/mdc-slider/slider.ts b/src/material-experimental/mdc-slider/slider.ts index 9af3baf389f6..7e5ee488f794 100644 --- a/src/material-experimental/mdc-slider/slider.ts +++ b/src/material-experimental/mdc-slider/slider.ts @@ -238,17 +238,6 @@ export class MatSliderVisualThumb implements AfterViewInit, OnDestroy { } } -/** - * Provider Expression that allows slider thumb inputs to register as a ControlValueAccessor. - * This allows it to support [(ngModel)] and [formControl]. - * @docs-private - */ -export const MAT_SLIDER_THUMB_VALUE_ACCESSOR: any = { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MatSliderThumb), - multi: true -}; - /** * Directive that adds slider-specific behaviors to an input element inside ``. * Up to two may be placed inside of a ``. @@ -265,7 +254,11 @@ export const MAT_SLIDER_THUMB_VALUE_ACCESSOR: any = { '(blur)': '_onBlur()', '(focus)': '_focus.emit()', }, - providers: [MAT_SLIDER_THUMB_VALUE_ACCESSOR], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: MatSliderThumb, + multi: true + }], }) export class MatSliderThumb implements AfterViewInit, ControlValueAccessor { @@ -322,7 +315,7 @@ export class MatSliderThumb implements AfterViewInit, ControlValueAccessor { * A callback function that is called by the forms API on * initialization to update the form model on blur (ControlValueAccessor). */ - private _onTouched: () => any = () => {}; + private _onTouched: () => void = () => {}; /** Indicates which slider thumb this input corresponds to. */ _thumbPosition: Thumb = this._elementRef.nativeElement.hasAttribute('matSliderStartThumb') @@ -388,6 +381,15 @@ export class MatSliderThumb implements AfterViewInit, ControlValueAccessor { this._onTouched = fn; } + /** + * Sets whether the component should be disabled. + * Implemented as part of ControlValueAccessor. + * @param isDisabled + */ + setDisabledState(isDisabled: boolean): void { + this._slider.disabled = isDisabled; + } + /** Returns true if this slider input currently has focus. */ _isFocused(): boolean { return this._document.activeElement === this._hostElement; From 763d7820720b595530df42390f514aa2d7359e3c Mon Sep 17 00:00:00 2001 From: wagnermaciel Date: Thu, 25 Feb 2021 10:45:56 -0800 Subject: [PATCH 3/4] fix(material-experimental/mdc-slider): code review changes * delete unused 'forwardRef' from imports --- src/material-experimental/mdc-slider/slider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/material-experimental/mdc-slider/slider.ts b/src/material-experimental/mdc-slider/slider.ts index 7e5ee488f794..533f40f17184 100644 --- a/src/material-experimental/mdc-slider/slider.ts +++ b/src/material-experimental/mdc-slider/slider.ts @@ -23,7 +23,6 @@ import { Directive, ElementRef, EventEmitter, - forwardRef, Inject, Input, NgZone, From ba8445dfe62e8890640c6f7f79b3881aa3317a1c Mon Sep 17 00:00:00 2001 From: wagnermaciel Date: Mon, 1 Mar 2021 12:47:51 -0800 Subject: [PATCH 4/4] fix(material-experimental/mdc-slider): code review changes * change #setDisabledState implementation so that if either input gets disabled, the whole slider gets disabled --- src/material-experimental/mdc-slider/slider.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/material-experimental/mdc-slider/slider.ts b/src/material-experimental/mdc-slider/slider.ts index 533f40f17184..4d3c96343a08 100644 --- a/src/material-experimental/mdc-slider/slider.ts +++ b/src/material-experimental/mdc-slider/slider.ts @@ -304,6 +304,8 @@ export class MatSliderThumb implements AfterViewInit, ControlValueAccessor { /** Event emitted every time the MatSliderThumb is focused. */ @Output() readonly _focus: EventEmitter = new EventEmitter(); + _disabled: boolean = false; + /** * A callback function that is called when the * control's value changes in the UI (ControlValueAccessor). @@ -386,7 +388,8 @@ export class MatSliderThumb implements AfterViewInit, ControlValueAccessor { * @param isDisabled */ setDisabledState(isDisabled: boolean): void { - this._slider.disabled = isDisabled; + this._disabled = isDisabled; + this._slider._updateDisabled(); } /** Returns true if this slider input currently has focus. */ @@ -620,6 +623,11 @@ export class MatSlider extends _MatSliderMixinBase implements AfterViewInit, OnD return this._inputs.length === 2; } + /** Sets the disabled state based on the disabled state of the inputs (ControlValueAccessor). */ + _updateDisabled(): void { + this.disabled = this._inputs.some(input => input._disabled); + } + /** Gets the slider thumb input of the given thumb position. */ _getInput(thumbPosition: Thumb): MatSliderThumb { return thumbPosition === Thumb.END ? this._inputs.last : this._inputs.first;