@@ -35,9 +35,7 @@ import {
35
35
import { SpecificEventListener , EventType } from '@material/base' ;
36
36
import { MDCSliderAdapter , MDCSliderFoundation , Thumb , TickMark } from '@material/slider' ;
37
37
38
- /**
39
- * Represents a drag event emitted by the MatSlider component.
40
- */
38
+ /** Represents a drag event emitted by the MatSlider component. */
41
39
export interface MatSliderDragEvent {
42
40
/** The MatSliderThumb that was interacted with. */
43
41
source : MatSliderThumb ;
@@ -50,7 +48,12 @@ export interface MatSliderDragEvent {
50
48
}
51
49
52
50
/**
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.
54
57
*/
55
58
@Directive ( {
56
59
selector : 'input[matSliderThumb], input[matSliderStartThumb], input[matSliderEndThumb]' ,
@@ -65,12 +68,12 @@ export class MatSliderThumb implements AfterViewInit {
65
68
66
69
// ** IMPORTANT NOTE **
67
70
//
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.
74
77
//
75
78
// Also, note that the value attribute is completely disconnected from the value property.
76
79
@@ -107,7 +110,9 @@ export class MatSliderThumb implements AfterViewInit {
107
110
@Output ( ) readonly _focus : EventEmitter < void > = new EventEmitter < void > ( ) ;
108
111
109
112
/** 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 ;
111
116
112
117
private _document : Document ;
113
118
@@ -117,21 +122,40 @@ export class MatSliderThumb implements AfterViewInit {
117
122
readonly _elementRef : ElementRef < HTMLInputElement > ,
118
123
) {
119
124
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 ( ) ;
132
128
}
133
129
134
130
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 {
135
159
const min = this . _elementRef . nativeElement . hasAttribute ( 'matSliderEndThumb' )
136
160
? this . _slider . _getInput ( Thumb . START ) . value
137
161
: this . _slider . min ;
@@ -140,19 +164,33 @@ export class MatSliderThumb implements AfterViewInit {
140
164
: this . _slider . max ;
141
165
this . _elementRef . nativeElement . min = `${ min } ` ;
142
166
this . _elementRef . nativeElement . max = `${ max } ` ;
167
+ }
143
168
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 {
145
178
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
- }
151
179
}
152
180
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
+ }
156
194
}
157
195
158
196
static ngAcceptInputType_value : NumberInput ;
@@ -265,6 +303,12 @@ export class MatSlider implements AfterViewInit, OnDestroy {
265
303
/** Used to keep track of & render the active & inactive tick marks on the slider track. */
266
304
_tickMarks : TickMark [ ] ;
267
305
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
+
268
312
constructor (
269
313
readonly _cdr : ChangeDetectorRef ,
270
314
readonly _elementRef : ElementRef < HTMLElement > ,
@@ -340,18 +384,21 @@ export class MatSlider implements AfterViewInit, OnDestroy {
340
384
return knobElementRef . nativeElement ;
341
385
}
342
386
387
+ _getValueIndicatorText ( thumb : Thumb ) {
388
+ return thumb === Thumb . START ? this . _startValueIndicatorText : this . _endValueIndicatorText ;
389
+ }
390
+
343
391
/**
344
392
* Sets the value indicator text of the given thumb using the given value.
345
393
*
346
394
* Uses the `displayWith` function if one has been provided. Otherwise, it just uses the
347
395
* numeric value as a string.
348
396
*/
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 ) {
353
398
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 ;
355
402
}
356
403
357
404
/** Determines the class name for a HTML element. */
@@ -523,7 +570,7 @@ function _validateInputs(
523
570
startInputElement : HTMLInputElement ,
524
571
endInputElement : HTMLInputElement ) : void {
525
572
if ( isRange ) {
526
- if ( ! startInputElement ! . hasAttribute ( 'matSliderStartThumb' ) ) {
573
+ if ( ! startInputElement . hasAttribute ( 'matSliderStartThumb' ) ) {
527
574
_throwInvalidInputConfigurationError ( ) ;
528
575
}
529
576
if ( ! endInputElement . hasAttribute ( 'matSliderEndThumb' ) ) {
0 commit comments