@@ -12,13 +12,18 @@ import {
12
12
dispatchPointerEvent ,
13
13
dispatchTouchEvent ,
14
14
} from '@angular/cdk/testing/private' ;
15
- import { Component , Type } from '@angular/core' ;
15
+ import { Component , QueryList , Type , ViewChild , ViewChildren } from '@angular/core' ;
16
16
import { ComponentFixture , fakeAsync , TestBed , tick , waitForAsync } from '@angular/core/testing' ;
17
17
import { By } from '@angular/platform-browser' ;
18
18
import { Thumb } from '@material/slider' ;
19
19
import { MatSliderModule } from './module' ;
20
20
import { MatSlider , MatSliderThumb , MatSliderVisualThumb } from './slider' ;
21
21
22
+ interface Point {
23
+ x : number ;
24
+ y : number ;
25
+ }
26
+
22
27
describe ( 'MDC-based MatSlider' , ( ) => {
23
28
let platform : Platform ;
24
29
@@ -759,6 +764,71 @@ describe('MDC-based MatSlider' , () => {
759
764
expect ( endInputInstance . value ) . toBe ( 70 ) ;
760
765
} ) ;
761
766
} ) ;
767
+
768
+ describe ( 'slider with a two-way binding' , ( ) => {
769
+ let fixture : ComponentFixture < SliderWithTwoWayBinding > ;
770
+ let testComponent : SliderWithTwoWayBinding ;
771
+
772
+ beforeEach ( ( ) => {
773
+ fixture = createComponent ( SliderWithTwoWayBinding ) ;
774
+ fixture . detectChanges ( ) ;
775
+ testComponent = fixture . componentInstance ;
776
+ } ) ;
777
+
778
+ it ( 'should sync the value binding in both directions' , ( ) => {
779
+ expect ( testComponent . value ) . toBe ( 0 ) ;
780
+ expect ( testComponent . sliderInput . value ) . toBe ( 0 ) ;
781
+
782
+ slideToValue ( testComponent . slider , 10 , Thumb . END , platform . IOS ) ;
783
+ expect ( testComponent . value ) . toBe ( 10 ) ;
784
+ expect ( testComponent . sliderInput . value ) . toBe ( 10 ) ;
785
+
786
+ testComponent . value = 20 ;
787
+ fixture . detectChanges ( ) ;
788
+ expect ( testComponent . value ) . toBe ( 20 ) ;
789
+ expect ( testComponent . sliderInput . value ) . toBe ( 20 ) ;
790
+ } ) ;
791
+ } ) ;
792
+
793
+ describe ( 'range slider with a two-way binding' , ( ) => {
794
+ let fixture : ComponentFixture < RangeSliderWithTwoWayBinding > ;
795
+ let testComponent : RangeSliderWithTwoWayBinding ;
796
+
797
+ beforeEach ( waitForAsync ( ( ) => {
798
+ fixture = createComponent ( RangeSliderWithTwoWayBinding ) ;
799
+ fixture . detectChanges ( ) ;
800
+ testComponent = fixture . componentInstance ;
801
+ } ) ) ;
802
+
803
+ it ( 'should sync the start value binding in both directions' , ( ) => {
804
+ expect ( testComponent . startValue ) . toBe ( 0 ) ;
805
+ expect ( testComponent . sliderInputs . get ( 0 ) ! . value ) . toBe ( 0 ) ;
806
+
807
+ slideToValue ( testComponent . slider , 10 , Thumb . START , platform . IOS ) ;
808
+
809
+ expect ( testComponent . startValue ) . toBe ( 10 ) ;
810
+ expect ( testComponent . sliderInputs . get ( 0 ) ! . value ) . toBe ( 10 ) ;
811
+
812
+ testComponent . startValue = 20 ;
813
+ fixture . detectChanges ( ) ;
814
+ expect ( testComponent . startValue ) . toBe ( 20 ) ;
815
+ expect ( testComponent . sliderInputs . get ( 0 ) ! . value ) . toBe ( 20 ) ;
816
+ } ) ;
817
+
818
+ it ( 'should sync the end value binding in both directions' , ( ) => {
819
+ expect ( testComponent . endValue ) . toBe ( 100 ) ;
820
+ expect ( testComponent . sliderInputs . get ( 1 ) ! . value ) . toBe ( 100 ) ;
821
+
822
+ slideToValue ( testComponent . slider , 90 , Thumb . END , platform . IOS ) ;
823
+ expect ( testComponent . endValue ) . toBe ( 90 ) ;
824
+ expect ( testComponent . sliderInputs . get ( 1 ) ! . value ) . toBe ( 90 ) ;
825
+
826
+ testComponent . endValue = 80 ;
827
+ fixture . detectChanges ( ) ;
828
+ expect ( testComponent . endValue ) . toBe ( 80 ) ;
829
+ expect ( testComponent . sliderInputs . get ( 1 ) ! . value ) . toBe ( 80 ) ;
830
+ } ) ;
831
+ } ) ;
762
832
} ) ;
763
833
764
834
@@ -910,6 +980,34 @@ class RangeSliderWithOneWayBinding {
910
980
endValue = 75 ;
911
981
}
912
982
983
+ @Component ( {
984
+ template : `
985
+ <mat-slider>
986
+ <input [(value)]="value" matSliderThumb>
987
+ </mat-slider>
988
+ ` ,
989
+ } )
990
+ class SliderWithTwoWayBinding {
991
+ @ViewChild ( MatSlider ) slider : MatSlider ;
992
+ @ViewChild ( MatSliderThumb ) sliderInput : MatSliderThumb ;
993
+ value = 0 ;
994
+ }
995
+
996
+ @Component ( {
997
+ template : `
998
+ <mat-slider>
999
+ <input [(value)]="startValue" matSliderStartThumb>
1000
+ <input [(value)]="endValue" matSliderEndThumb>
1001
+ </mat-slider>
1002
+ ` ,
1003
+ } )
1004
+ class RangeSliderWithTwoWayBinding {
1005
+ @ViewChild ( MatSlider ) slider : MatSlider ;
1006
+ @ViewChildren ( MatSliderThumb ) sliderInputs : QueryList < MatSliderThumb > ;
1007
+ startValue = 0 ;
1008
+ endValue = 100 ;
1009
+ }
1010
+
913
1011
/** The pointer event types used by the MDC Slider. */
914
1012
const enum PointerEventType {
915
1013
POINTER_DOWN = 'pointerdown' ,
@@ -926,40 +1024,36 @@ const enum TouchEventType {
926
1024
927
1025
/** Clicks on the MatSlider at the coordinates corresponding to the given value. */
928
1026
function setValueByClick ( slider : MatSlider , value : number , isIOS : boolean ) {
929
- const { min, max} = slider ;
930
- const percent = ( value - min ) / ( max - min ) ;
931
-
932
1027
const sliderElement = slider . _elementRef . nativeElement ;
933
- const { top, left, width, height} = sliderElement . getBoundingClientRect ( ) ;
934
- const x = left + ( width * percent ) ;
935
- const y = top + ( height / 2 ) ;
1028
+ const { x, y} = getCoordsForValue ( slider , value ) ;
936
1029
937
1030
dispatchPointerOrTouchEvent ( sliderElement , PointerEventType . POINTER_DOWN , x , y , isIOS ) ;
938
1031
dispatchPointerOrTouchEvent ( sliderElement , PointerEventType . POINTER_UP , x , y , isIOS ) ;
939
1032
}
940
1033
941
1034
/** Slides the MatSlider's thumb to the given value. */
942
1035
function slideToValue ( slider : MatSlider , value : number , thumbPosition : Thumb , isIOS : boolean ) {
943
- const { min, max} = slider ;
944
- const percent = ( value - min ) / ( max - min ) ;
945
-
946
1036
const sliderElement = slider . _elementRef . nativeElement ;
947
- const thumbElement = slider . _getThumbElement ( thumbPosition ) ;
948
-
949
- const sliderDimensions = sliderElement . getBoundingClientRect ( ) ;
950
- const thumbDimensions = thumbElement . getBoundingClientRect ( ) ;
951
-
952
- const startX = thumbDimensions . left + ( thumbDimensions . width / 2 ) ;
953
- const startY = thumbDimensions . top + ( thumbDimensions . height / 2 ) ;
954
-
955
- const endX = sliderDimensions . left + ( sliderDimensions . width * percent ) ;
956
- const endY = sliderDimensions . top + ( sliderDimensions . height / 2 ) ;
1037
+ const { x : startX , y : startY } = getCoordsForValue ( slider , slider . _getInput ( thumbPosition ) . value ) ;
1038
+ const { x : endX , y : endY } = getCoordsForValue ( slider , value ) ;
957
1039
958
1040
dispatchPointerOrTouchEvent ( sliderElement , PointerEventType . POINTER_DOWN , startX , startY , isIOS ) ;
959
1041
dispatchPointerOrTouchEvent ( sliderElement , PointerEventType . POINTER_MOVE , endX , endY , isIOS ) ;
960
1042
dispatchPointerOrTouchEvent ( sliderElement , PointerEventType . POINTER_UP , endX , endY , isIOS ) ;
961
1043
}
962
1044
1045
+ /** Returns the x and y coordinates for the given slider value. */
1046
+ function getCoordsForValue ( slider : MatSlider , value : number ) : Point {
1047
+ const { min, max} = slider ;
1048
+ const percent = ( value - min ) / ( max - min ) ;
1049
+
1050
+ const { top, left, width, height} = slider . _elementRef . nativeElement . getBoundingClientRect ( ) ;
1051
+ const x = left + ( width * percent ) ;
1052
+ const y = top + ( height / 2 ) ;
1053
+
1054
+ return { x, y} ;
1055
+ }
1056
+
963
1057
/** Dispatch a pointerdown or pointerup event if supported, otherwise dispatch the touch event. */
964
1058
function dispatchPointerOrTouchEvent (
965
1059
node : Node , type : PointerEventType , x : number , y : number , isIOS : boolean ) {
0 commit comments