Skip to content

Commit

Permalink
feat(slider): emit input event when slider thumb moves
Browse files Browse the repository at this point in the history
* 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 angular#2296
  • Loading branch information
devversion committed Dec 21, 2016
1 parent 4571561 commit 0b60b6f
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 9 deletions.
54 changes: 52 additions & 2 deletions src/lib/slider/slider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,55 @@ describe('MdSlider', () => {
});
});

describe('slider with input event', () => {
let fixture: ComponentFixture<SliderWithChangeHandler>;
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 = <HTMLElement>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<StandardSlider>;
let sliderDebugElement: DebugElement;
Expand Down Expand Up @@ -1108,11 +1157,12 @@ class SliderWithValueSmallerThanMin { }
class SliderWithValueGreaterThanMax { }

@Component({
template: `<md-slider (change)="onChange($event)"></md-slider>`,
template: `<md-slider (change)="onChange($event)" (input)="onInput($event)"></md-slider>`,
styles: [styles],
})
class SliderWithChangeHandler {
onChange() { }
onChange() { };
onInput() { };
}

@Component({
Expand Down
39 changes: 32 additions & 7 deletions src/lib/slider/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,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 = () => {};
Expand Down Expand Up @@ -295,6 +296,7 @@ export class MdSlider implements ControlValueAccessor {
}

@Output() change = new EventEmitter<MdSliderChange>();
@Output() input = new EventEmitter<MdSliderChange>();

constructor(@Optional() private _dir: Dir, elementRef: ElementRef) {
this._renderer = new SliderRenderer(elementRef);
Expand All @@ -320,6 +322,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();
}

Expand All @@ -331,6 +336,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) {
Expand Down Expand Up @@ -434,16 +442,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) {
Expand All @@ -461,6 +476,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);
Expand Down

0 comments on commit 0b60b6f

Please sign in to comment.