From 99963c4fd182b490241e4b25f08e948020d7ced8 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 6 Jan 2017 03:06:30 +0100 Subject: [PATCH] feat(slider): emit input event when slider thumb moves (#2325) Closes #2296 --- src/lib/slider/slider.spec.ts | 54 +++++++++++++++++++++++++++++++++-- src/lib/slider/slider.ts | 41 +++++++++++++++++++++----- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index ec04fad22f52..85ae1c2e5efe 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -785,6 +785,55 @@ describe('MdSlider', () => { }); }); + describe('slider with input event', () => { + let fixture: ComponentFixture; + let sliderDebugElement: DebugElement; + let sliderNativeElement: HTMLElement; + let sliderTrackElement: HTMLElement; + let testComponent: SliderWithChangeHandler; + + beforeEach(() => { + fixture = TestBed.createComponent(SliderWithChangeHandler); + fixture.detectChanges(); + + testComponent = fixture.debugElement.componentInstance; + spyOn(testComponent, 'onInput'); + spyOn(testComponent, 'onChange'); + + sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); + sliderNativeElement = sliderDebugElement.nativeElement; + sliderTrackElement = sliderNativeElement.querySelector('.md-slider-track'); + }); + + it('should emit an input event while sliding', () => { + expect(testComponent.onChange).not.toHaveBeenCalled(); + + dispatchMouseenterEvent(sliderNativeElement); + dispatchSlideEvent(sliderNativeElement, 0.5, gestureConfig); + dispatchSlideEvent(sliderNativeElement, 1, gestureConfig); + dispatchSlideEndEvent(sliderNativeElement, 1, gestureConfig); + + fixture.detectChanges(); + + // The input event should fire twice, because the slider changed two times. + expect(testComponent.onInput).toHaveBeenCalledTimes(2); + expect(testComponent.onChange).toHaveBeenCalledTimes(1); + }); + + it('should emit an input event when clicking', () => { + expect(testComponent.onChange).not.toHaveBeenCalled(); + + dispatchClickEventSequence(sliderNativeElement, 0.75); + + fixture.detectChanges(); + + // The `onInput` event should be emitted once due to a single click. + expect(testComponent.onInput).toHaveBeenCalledTimes(1); + expect(testComponent.onChange).toHaveBeenCalledTimes(1); + }); + + }); + describe('keyboard support', () => { let fixture: ComponentFixture; let sliderDebugElement: DebugElement; @@ -1134,11 +1183,12 @@ class SliderWithValueSmallerThanMin { } class SliderWithValueGreaterThanMax { } @Component({ - template: ``, + template: ``, styles: [styles], }) class SliderWithChangeHandler { - onChange() { } + onChange() { }; + onInput() { }; } @Component({ diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index fb7a2919a9af..bf5d6c42b414 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -117,8 +117,9 @@ export class MdSlider implements ControlValueAccessor { private _controlValueAccessorChangeFn: (value: any) => void = () => {}; - /** The last value for which a change event was emitted. */ - private _lastEmittedValue: number = null; + /** The last values for which a change or input event was emitted. */ + private _lastChangeValue: number = null; + private _lastInputValue: number = null; /** onTouch function registered via registerOnTouch (ControlValueAccessor). */ onTouched: () => any = () => {}; @@ -301,6 +302,9 @@ export class MdSlider implements ControlValueAccessor { /** Event emitted when the slider value has changed. */ @Output() change = new EventEmitter(); + /** Event emitted when the slider thumb moves. */ + @Output() input = new EventEmitter(); + constructor(@Optional() private _dir: Dir, elementRef: ElementRef) { this._renderer = new SliderRenderer(elementRef); } @@ -325,6 +329,9 @@ export class MdSlider implements ControlValueAccessor { this._isSliding = false; this._renderer.addFocus(); this._updateValueFromPosition({x: event.clientX, y: event.clientY}); + + /* Emits a change and input event if the value changed. */ + this._emitInputEvent(); this._emitValueIfChanged(); } @@ -336,6 +343,9 @@ export class MdSlider implements ControlValueAccessor { // Prevent the slide from selecting anything else. event.preventDefault(); this._updateValueFromPosition({x: event.center.x, y: event.center.y}); + + // Native range elements always emit `input` events when the value changed while sliding. + this._emitInputEvent(); } _onSlideStart(event: HammerInput) { @@ -439,16 +449,23 @@ export class MdSlider implements ControlValueAccessor { /** Emits a change event if the current value is different from the last emitted value. */ private _emitValueIfChanged() { - if (this.value != this._lastEmittedValue) { - let event = new MdSliderChange(); - event.source = this; - event.value = this.value; - this._lastEmittedValue = this.value; + if (this.value != this._lastChangeValue) { + let event = this._createChangeEvent(); + this._lastChangeValue = this.value; this._controlValueAccessorChangeFn(this.value); this.change.emit(event); } } + /** Emits an input event when the current value is different from the last emitted value. */ + private _emitInputEvent() { + if (this.value != this._lastInputValue) { + let event = this._createChangeEvent(); + this._lastInputValue = this.value; + this.input.emit(event); + } + } + /** Updates the amount of space between ticks as a percentage of the width of the slider. */ private _updateTickIntervalPercent() { if (!this.tickInterval) { @@ -466,6 +483,16 @@ export class MdSlider implements ControlValueAccessor { } } + /** Creates a slider change object from the specified value. */ + private _createChangeEvent(value = this.value): MdSliderChange { + let event = new MdSliderChange(); + + event.source = this; + event.value = value; + + return event; + } + /** Calculates the percentage of the slider that a value is. */ private _calculatePercentage(value: number) { return (value - this.min) / (this.max - this.min);