Skip to content

Commit 0b60b6f

Browse files
committed
feat(slider): emit input event when slider thumb moves
* Adds an input event output, which fires each time the thumb changes its position. Made similar to the native range element `input` event dispatching. Closes #2296
1 parent 4571561 commit 0b60b6f

File tree

2 files changed

+84
-9
lines changed

2 files changed

+84
-9
lines changed

src/lib/slider/slider.spec.ts

+52-2
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,55 @@ describe('MdSlider', () => {
759759
});
760760
});
761761

762+
describe('slider with input event', () => {
763+
let fixture: ComponentFixture<SliderWithChangeHandler>;
764+
let sliderDebugElement: DebugElement;
765+
let sliderNativeElement: HTMLElement;
766+
let sliderTrackElement: HTMLElement;
767+
let testComponent: SliderWithChangeHandler;
768+
769+
beforeEach(() => {
770+
fixture = TestBed.createComponent(SliderWithChangeHandler);
771+
fixture.detectChanges();
772+
773+
testComponent = fixture.debugElement.componentInstance;
774+
spyOn(testComponent, 'onInput');
775+
spyOn(testComponent, 'onChange');
776+
777+
sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider));
778+
sliderNativeElement = sliderDebugElement.nativeElement;
779+
sliderTrackElement = <HTMLElement>sliderNativeElement.querySelector('.md-slider-track');
780+
});
781+
782+
it('should emit an input event while sliding', () => {
783+
expect(testComponent.onChange).not.toHaveBeenCalled();
784+
785+
dispatchMouseenterEvent(sliderNativeElement);
786+
dispatchSlideEvent(sliderNativeElement, 0.5, gestureConfig);
787+
dispatchSlideEvent(sliderNativeElement, 1, gestureConfig);
788+
dispatchSlideEndEvent(sliderNativeElement, 1, gestureConfig);
789+
790+
fixture.detectChanges();
791+
792+
// The input event should fire twice, because the slider changed two times.
793+
expect(testComponent.onInput).toHaveBeenCalledTimes(2);
794+
expect(testComponent.onChange).toHaveBeenCalledTimes(1);
795+
});
796+
797+
it('should emit an input event when clicking', () => {
798+
expect(testComponent.onChange).not.toHaveBeenCalled();
799+
800+
dispatchClickEventSequence(sliderNativeElement, 0.75);
801+
802+
fixture.detectChanges();
803+
804+
// The `onInput` event should be emitted once due to a single click.
805+
expect(testComponent.onInput).toHaveBeenCalledTimes(1);
806+
expect(testComponent.onChange).toHaveBeenCalledTimes(1);
807+
});
808+
809+
});
810+
762811
describe('keyboard support', () => {
763812
let fixture: ComponentFixture<StandardSlider>;
764813
let sliderDebugElement: DebugElement;
@@ -1108,11 +1157,12 @@ class SliderWithValueSmallerThanMin { }
11081157
class SliderWithValueGreaterThanMax { }
11091158

11101159
@Component({
1111-
template: `<md-slider (change)="onChange($event)"></md-slider>`,
1160+
template: `<md-slider (change)="onChange($event)" (input)="onInput($event)"></md-slider>`,
11121161
styles: [styles],
11131162
})
11141163
class SliderWithChangeHandler {
1115-
onChange() { }
1164+
onChange() { };
1165+
onInput() { };
11161166
}
11171167

11181168
@Component({

src/lib/slider/slider.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ export class MdSlider implements ControlValueAccessor {
113113

114114
private _controlValueAccessorChangeFn: (value: any) => void = () => {};
115115

116-
/** The last value for which a change event was emitted. */
117-
private _lastEmittedValue: number = null;
116+
/** The last values for which a change or input event was emitted. */
117+
private _lastChangeValue: number = null;
118+
private _lastInputValue: number = null;
118119

119120
/** onTouch function registered via registerOnTouch (ControlValueAccessor). */
120121
onTouched: () => any = () => {};
@@ -295,6 +296,7 @@ export class MdSlider implements ControlValueAccessor {
295296
}
296297

297298
@Output() change = new EventEmitter<MdSliderChange>();
299+
@Output() input = new EventEmitter<MdSliderChange>();
298300

299301
constructor(@Optional() private _dir: Dir, elementRef: ElementRef) {
300302
this._renderer = new SliderRenderer(elementRef);
@@ -320,6 +322,9 @@ export class MdSlider implements ControlValueAccessor {
320322
this._isSliding = false;
321323
this._renderer.addFocus();
322324
this._updateValueFromPosition({x: event.clientX, y: event.clientY});
325+
326+
/* Emits a change and input event if the value changed. */
327+
this._emitInputEvent();
323328
this._emitValueIfChanged();
324329
}
325330

@@ -331,6 +336,9 @@ export class MdSlider implements ControlValueAccessor {
331336
// Prevent the slide from selecting anything else.
332337
event.preventDefault();
333338
this._updateValueFromPosition({x: event.center.x, y: event.center.y});
339+
340+
// Native range elements always emit `input` events when the value changed while sliding.
341+
this._emitInputEvent();
334342
}
335343

336344
_onSlideStart(event: HammerInput) {
@@ -434,16 +442,23 @@ export class MdSlider implements ControlValueAccessor {
434442

435443
/** Emits a change event if the current value is different from the last emitted value. */
436444
private _emitValueIfChanged() {
437-
if (this.value != this._lastEmittedValue) {
438-
let event = new MdSliderChange();
439-
event.source = this;
440-
event.value = this.value;
441-
this._lastEmittedValue = this.value;
445+
if (this.value != this._lastChangeValue) {
446+
let event = this._createChangeEvent();
447+
this._lastChangeValue = this.value;
442448
this._controlValueAccessorChangeFn(this.value);
443449
this.change.emit(event);
444450
}
445451
}
446452

453+
/** Emits an input event when the current value is different from the last emitted value. */
454+
private _emitInputEvent() {
455+
if (this.value != this._lastInputValue) {
456+
let event = this._createChangeEvent();
457+
this._lastInputValue = this.value;
458+
this.input.emit(event);
459+
}
460+
}
461+
447462
/** Updates the amount of space between ticks as a percentage of the width of the slider. */
448463
private _updateTickIntervalPercent() {
449464
if (!this.tickInterval) {
@@ -461,6 +476,16 @@ export class MdSlider implements ControlValueAccessor {
461476
}
462477
}
463478

479+
/** Creates a slider change object from the specified value. */
480+
private _createChangeEvent(value = this.value): MdSliderChange {
481+
let event = new MdSliderChange();
482+
483+
event.source = this;
484+
event.value = value;
485+
486+
return event;
487+
}
488+
464489
/** Calculates the percentage of the slider that a value is. */
465490
private _calculatePercentage(value: number) {
466491
return (value - this.min) / (this.max - this.min);

0 commit comments

Comments
 (0)