@@ -6,7 +6,13 @@ import {
6
6
Input ,
7
7
ViewEncapsulation ,
8
8
AfterContentInit ,
9
+ forwardRef ,
9
10
} from '@angular/core' ;
11
+ import {
12
+ NG_VALUE_ACCESSOR ,
13
+ ControlValueAccessor ,
14
+ FormsModule ,
15
+ } from '@angular/forms' ;
10
16
import { HAMMER_GESTURE_CONFIG } from '@angular/platform-browser' ;
11
17
import { BooleanFieldValue } from '@angular2-material/core/annotations/field-value' ;
12
18
import { applyCssTransform } from '@angular2-material/core/style/apply-transform' ;
@@ -18,9 +24,20 @@ import {MdGestureConfig} from '@angular2-material/core/core';
18
24
*/
19
25
const MIN_AUTO_TICK_SEPARATION = 30 ;
20
26
27
+ /**
28
+ * Provider Expression that allows md-slider to register as a ControlValueAccessor.
29
+ * This allows it to support [(ngModel)] and [formControl].
30
+ */
31
+ export const MD_SLIDER_VALUE_ACCESSOR : any = {
32
+ provide : NG_VALUE_ACCESSOR ,
33
+ useExisting : forwardRef ( ( ) => MdSlider ) ,
34
+ multi : true
35
+ } ;
36
+
21
37
@Component ( {
22
38
moduleId : module . id ,
23
39
selector : 'md-slider' ,
40
+ providers : [ MD_SLIDER_VALUE_ACCESSOR ] ,
24
41
host : {
25
42
'tabindex' : '0' ,
26
43
'(click)' : 'onClick($event)' ,
@@ -34,7 +51,7 @@ const MIN_AUTO_TICK_SEPARATION = 30;
34
51
styleUrls : [ 'slider.css' ] ,
35
52
encapsulation : ViewEncapsulation . None ,
36
53
} )
37
- export class MdSlider implements AfterContentInit {
54
+ export class MdSlider implements AfterContentInit , ControlValueAccessor {
38
55
/** A renderer to handle updating the slider's thumb and fill track. */
39
56
private _renderer : SliderRenderer = null ;
40
57
@@ -61,6 +78,11 @@ export class MdSlider implements AfterContentInit {
61
78
/** The percentage of the slider that coincides with the value. */
62
79
private _percent : number = 0 ;
63
80
81
+ private _controlValueAccessorChangeFn : ( value : any ) => void = ( value ) => { } ;
82
+
83
+ /** onTouch function registered via registerOnTouch (ControlValueAccessor). */
84
+ onTouched : ( ) => any = ( ) => { } ;
85
+
64
86
/** The values at which the thumb will snap. */
65
87
@Input ( ) step : number = 1 ;
66
88
@@ -123,8 +145,15 @@ export class MdSlider implements AfterContentInit {
123
145
}
124
146
125
147
set value ( v : number ) {
148
+ // Only set the value to a valid number. v is casted to an any as we know it will come in as a
149
+ // string but it is labeled as a number which causes parseFloat to not accept it.
150
+ if ( isNaN ( parseFloat ( < any > v ) ) ) {
151
+ return ;
152
+ }
153
+
126
154
this . _value = Number ( v ) ;
127
155
this . _isInitialized = true ;
156
+ this . _controlValueAccessorChangeFn ( this . _value ) ;
128
157
}
129
158
130
159
constructor ( elementRef : ElementRef ) {
@@ -138,7 +167,10 @@ export class MdSlider implements AfterContentInit {
138
167
*/
139
168
ngAfterContentInit ( ) {
140
169
this . _sliderDimensions = this . _renderer . getSliderDimensions ( ) ;
141
- this . snapToValue ( ) ;
170
+ // This needs to be called after content init because the value can be set to the min if the
171
+ // value itself isn't set. If this happens, the control value accessor needs to be updated.
172
+ this . _controlValueAccessorChangeFn ( this . value ) ;
173
+ this . snapThumbToValue ( ) ;
142
174
this . _updateTickSeparation ( ) ;
143
175
}
144
176
@@ -152,7 +184,7 @@ export class MdSlider implements AfterContentInit {
152
184
this . isSliding = false ;
153
185
this . _renderer . addFocus ( ) ;
154
186
this . updateValueFromPosition ( event . clientX ) ;
155
- this . snapToValue ( ) ;
187
+ this . snapThumbToValue ( ) ;
156
188
}
157
189
158
190
/** TODO: internal */
@@ -182,7 +214,7 @@ export class MdSlider implements AfterContentInit {
182
214
/** TODO: internal */
183
215
onSlideEnd ( ) {
184
216
this . isSliding = false ;
185
- this . snapToValue ( ) ;
217
+ this . snapThumbToValue ( ) ;
186
218
}
187
219
188
220
/** TODO: internal */
@@ -196,6 +228,7 @@ export class MdSlider implements AfterContentInit {
196
228
/** TODO: internal */
197
229
onBlur ( ) {
198
230
this . isActive = false ;
231
+ this . onTouched ( ) ;
199
232
}
200
233
201
234
/**
@@ -230,7 +263,7 @@ export class MdSlider implements AfterContentInit {
230
263
* Snaps the thumb to the current value.
231
264
* Called after a click or drag event is over.
232
265
*/
233
- snapToValue ( ) {
266
+ snapThumbToValue ( ) {
234
267
this . updatePercentFromValue ( ) ;
235
268
this . _renderer . updateThumbAndFillPosition ( this . _percent , this . _sliderDimensions . width ) ;
236
269
}
@@ -315,6 +348,34 @@ export class MdSlider implements AfterContentInit {
315
348
clamp ( value : number , min = 0 , max = 1 ) {
316
349
return Math . max ( min , Math . min ( value , max ) ) ;
317
350
}
351
+
352
+ /**
353
+ * Implemented as part of ControlValueAccessor.
354
+ * TODO: internal
355
+ */
356
+ writeValue ( value : any ) {
357
+ this . value = value ;
358
+
359
+ if ( this . _sliderDimensions ) {
360
+ this . snapThumbToValue ( ) ;
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Implemented as part of ControlValueAccessor.
366
+ * TODO: internal
367
+ */
368
+ registerOnChange ( fn : ( value : any ) => void ) {
369
+ this . _controlValueAccessorChangeFn = fn ;
370
+ }
371
+
372
+ /**
373
+ * Implemented as part of ControlValueAccessor.
374
+ * TODO: internal
375
+ */
376
+ registerOnTouched ( fn : any ) {
377
+ this . onTouched = fn ;
378
+ }
318
379
}
319
380
320
381
/**
@@ -392,6 +453,7 @@ export const MD_SLIDER_DIRECTIVES = [MdSlider];
392
453
393
454
394
455
@NgModule ( {
456
+ imports : [ FormsModule ] ,
395
457
exports : MD_SLIDER_DIRECTIVES ,
396
458
declarations : MD_SLIDER_DIRECTIVES ,
397
459
providers : [
0 commit comments