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