Skip to content

Commit 3099b97

Browse files
committed
fix(material-experimental/mdc-slider): code review changes
* revert to using a method to display the value indicator text instead of directly through the DOM * avoid styling based on tag name and instead target class names applied to host elements * fix indentation in slider.scss * condense jsdoc to oneline * expand MatSliderThumb jsdoc * reworded comment in MatSliderThumb * move initialization of _thumb value to property definition * move input value attribute initialization to separate method * move input min and max property initialization to separate method * move input value property initialization to separate method * remove unnecessary !
1 parent 6e8ab85 commit 3099b97

File tree

3 files changed

+88
-41
lines changed

3 files changed

+88
-41
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<div class="mdc-slider__thumb" *ngFor="let thumb of _getThumbTypes()" #thumb>
1717
<div class="mdc-slider__value-indicator-container" *ngIf="discrete">
1818
<div class="mdc-slider__value-indicator">
19-
<span class="mdc-slider__value-indicator-text" #valueIndicatorTextElement></span>
19+
<span class="mdc-slider__value-indicator-text">{{_getValueIndicatorText(thumb)}}</span>
2020
</div>
2121
</div>
2222
<div class="mdc-slider__thumb-knob" #knob></div>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@import '@material/slider/slider';
22
@include core-styles;
33

4-
mat-slider {
5-
display: block;
4+
.mdc-slider {
5+
display: block;
66
}

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

Lines changed: 85 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ import {
3535
import {SpecificEventListener, EventType} from '@material/base';
3636
import {MDCSliderAdapter, MDCSliderFoundation, Thumb, TickMark} from '@material/slider';
3737

38-
/**
39-
* Represents a drag event emitted by the MatSlider component.
40-
*/
38+
/** Represents a drag event emitted by the MatSlider component. */
4139
export interface MatSliderDragEvent {
4240
/** The MatSliderThumb that was interacted with. */
4341
source: MatSliderThumb;
@@ -50,7 +48,12 @@ export interface MatSliderDragEvent {
5048
}
5149

5250
/**
53-
* The native input used by the MatSlider.
51+
* Directive that adds slider-specific behaviors to an input element inside `<mat-slider>`.
52+
* Up to two may be placed inside of a `<mat-slider>`.
53+
*
54+
* If one is used, the selector `matSliderThumb` must be used, and the outcome will be a normal
55+
* slider. If two are used, the selectors `matSliderStartThumb` and `matSliderEndThumb` must be
56+
* used, and the outcome will be a range slider with two slider thumbs.
5457
*/
5558
@Directive({
5659
selector: 'input[matSliderThumb], input[matSliderStartThumb], input[matSliderEndThumb]',
@@ -65,12 +68,12 @@ export class MatSliderThumb implements AfterViewInit {
6568

6669
// ** IMPORTANT NOTE **
6770
//
68-
// The way `value` is implemented for MatSliderThumb goes against our standard practice. Normally
69-
// we would define a private variable `_value` as the source of truth for the value of the slider
70-
// thumb input. The source of truth for the value of the slider inputs has already been decided
71-
// for us by MDC to be the value attribute on the slider thumb inputs. This is because the MDC
72-
// foundation and adapter expect that the value attribute is the source of truth for the slider
73-
// inputs.
71+
// The way `value` is implemented for MatSliderThumb doesn't follow typical Angular conventions.
72+
// Normally we would define a private variable `_value` as the source of truth for the value of
73+
// the slider thumb input. The source of truth for the value of the slider inputs has already
74+
// been decided for us by MDC to be the value attribute on the slider thumb inputs. This is
75+
// because the MDC foundation and adapter expect that the value attribute is the source of truth
76+
// for the slider inputs.
7477
//
7578
// Also, note that the value attribute is completely disconnected from the value property.
7679

@@ -107,7 +110,9 @@ export class MatSliderThumb implements AfterViewInit {
107110
@Output() readonly _focus: EventEmitter<void> = new EventEmitter<void>();
108111

109112
/** Indicates which slider thumb this input corresponds to. */
110-
private _thumb: Thumb;
113+
private _thumb: Thumb = this._elementRef.nativeElement.hasAttribute('matSliderStartThumb')
114+
? Thumb.START
115+
: Thumb.END;
111116

112117
private _document: Document;
113118

@@ -117,21 +122,40 @@ export class MatSliderThumb implements AfterViewInit {
117122
readonly _elementRef: ElementRef<HTMLInputElement>,
118123
) {
119124
this._document = document;
120-
this._thumb = _elementRef.nativeElement.hasAttribute('matSliderStartThumb')
121-
? Thumb.START
122-
: Thumb.END;
123-
124-
// Only set the default value if an initial value has not already been provided.
125-
// Note that we are only setting the value attribute at this point. We cannot set the value
126-
// property yet because the min and max have not been set.
127-
if (!_elementRef.nativeElement.hasAttribute('value')) {
128-
this.value = _elementRef.nativeElement.hasAttribute('matSliderEndThumb')
129-
? _slider.max
130-
: _slider.min;
131-
}
125+
// By calling this in the constructor we guarantee that the sibling sliders initial value by
126+
// has already been set by the time we reach ngAfterViewInit().
127+
this._initializeInputValueAttribute();
132128
}
133129

134130
ngAfterViewInit() {
131+
this._initializeInputMinMax();
132+
this._initializeInputValueProperty();
133+
134+
// Setup for the MDC foundation.
135+
if (this._slider.disabled) {
136+
this._elementRef.nativeElement.disabled = true;
137+
}
138+
}
139+
140+
/** Returns true if this slider input currently has focus. */
141+
_isFocused(): boolean {
142+
return this._document.activeElement === this._elementRef.nativeElement;
143+
}
144+
145+
/**
146+
* Sets the min and max properties on the slider thumb input.
147+
*
148+
* Must be called AFTER the sibling slider thumb input is guaranteed to have had its value
149+
* attribute value set. For a range slider, the min and max of the slider thumb input depends on
150+
* the value of its sibling slider thumb inputs value.
151+
*
152+
* Must be called BEFORE the value property is set. In the case where the min and max have not
153+
* yet been set and we are setting the input value property to a value outside of the native
154+
* inputs default min or max. The value property would not be set to our desired value, but
155+
* instead be capped at either the default min or max.
156+
*
157+
*/
158+
private _initializeInputMinMax(): void {
135159
const min = this._elementRef.nativeElement.hasAttribute('matSliderEndThumb')
136160
? this._slider._getInput(Thumb.START).value
137161
: this._slider.min;
@@ -140,19 +164,33 @@ export class MatSliderThumb implements AfterViewInit {
140164
: this._slider.max;
141165
this._elementRef.nativeElement.min = `${min}`;
142166
this._elementRef.nativeElement.max = `${max}`;
167+
}
143168

144-
// We can now set the property value because the min and max have now been set.
169+
/**
170+
* Sets the value property on the slider thumb input.
171+
*
172+
* Must be called AFTER the min and max have been set. In the case where the min and max have not
173+
* yet been set and we are setting the input value property to a value outside of the native
174+
* inputs default min or max. The value property would not be set to our desired value, but
175+
* instead be capped at either the default min or max.
176+
*/
177+
private _initializeInputValueProperty(): void {
145178
this._elementRef.nativeElement.value = `${this.value}`;
146-
147-
// Setup for the MDC foundation.
148-
if (this._slider.disabled) {
149-
this._elementRef.nativeElement.disabled = true;
150-
}
151179
}
152180

153-
/** Returns true if this slider input currently has focus. */
154-
_isFocused(): boolean {
155-
return this._document.activeElement === this._elementRef.nativeElement;
181+
/**
182+
* Ensures the value attribute is initialized.
183+
*
184+
* Must be called BEFORE the min and max are set. For a range slider, the min and max of the
185+
* slider thumb input depends on the value of its sibling slider thumb inputs value.
186+
*/
187+
private _initializeInputValueAttribute(): void {
188+
// Only set the default value if an initial value has not already been provided.
189+
if (!this._elementRef.nativeElement.hasAttribute('value')) {
190+
this.value = this._elementRef.nativeElement.hasAttribute('matSliderEndThumb')
191+
? this._slider.max
192+
: this._slider.min;
193+
}
156194
}
157195

158196
static ngAcceptInputType_value: NumberInput;
@@ -265,6 +303,12 @@ export class MatSlider implements AfterViewInit, OnDestroy {
265303
/** Used to keep track of & render the active & inactive tick marks on the slider track. */
266304
_tickMarks: TickMark[];
267305

306+
/** The display value of the start thumb. */
307+
private _startValueIndicatorText: string;
308+
309+
/** The display value of the end thumb. */
310+
private _endValueIndicatorText: string;
311+
268312
constructor(
269313
readonly _cdr: ChangeDetectorRef,
270314
readonly _elementRef: ElementRef<HTMLElement>,
@@ -340,18 +384,21 @@ export class MatSlider implements AfterViewInit, OnDestroy {
340384
return knobElementRef.nativeElement;
341385
}
342386

387+
_getValueIndicatorText(thumb: Thumb) {
388+
return thumb === Thumb.START ? this._startValueIndicatorText : this._endValueIndicatorText;
389+
}
390+
343391
/**
344392
* Sets the value indicator text of the given thumb using the given value.
345393
*
346394
* Uses the `displayWith` function if one has been provided. Otherwise, it just uses the
347395
* numeric value as a string.
348396
*/
349-
_setValueIndicatorText(value: number, thumb: Thumb): void {
350-
const valueIndicatorTextElementRef = thumb === Thumb.END
351-
? this._valueIndicatorTextElements.last
352-
: this._valueIndicatorTextElements.first;
397+
_setValueIndicatorText(value: number, thumb: Thumb) {
353398
const valueText = this.displayWith ? this.displayWith(value) : `${value}`;
354-
valueIndicatorTextElementRef.nativeElement.textContent = valueText;
399+
thumb === Thumb.START
400+
? this._startValueIndicatorText = valueText
401+
: this._endValueIndicatorText = valueText;
355402
}
356403

357404
/** Determines the class name for a HTML element. */
@@ -523,7 +570,7 @@ function _validateInputs(
523570
startInputElement: HTMLInputElement,
524571
endInputElement: HTMLInputElement): void {
525572
if (isRange) {
526-
if (!startInputElement!.hasAttribute('matSliderStartThumb')) {
573+
if (!startInputElement.hasAttribute('matSliderStartThumb')) {
527574
_throwInvalidInputConfigurationError();
528575
}
529576
if (!endInputElement.hasAttribute('matSliderEndThumb')) {

0 commit comments

Comments
 (0)