Created
January 30, 2019 16:50
-
-
Save JanMalch/104de9ec29786469969aef759dcfcf37 to your computer and use it in GitHub Desktop.
Angular directive for input value as RxJS observable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Directive, ElementRef, Input, OnDestroy} from "@angular/core"; | |
import {fromEvent, Observable, Subject} from "rxjs"; | |
import {debounceTime, distinctUntilChanged, map, startWith, takeUntil} from "rxjs/operators"; | |
/** | |
* An input directive that makes it easier to get the input's value in an Observable. | |
* These directive acts a one-way for output. You can't set the input's value. | |
* @example | |
* <input rxjsInput> | |
* | |
* ViewChild(RxJsInputDirective) myInput$: RxJsInputDirective<string>; | |
* myInput$.asObservable('start observable with me!').subscribe(console.log); | |
* // the start value won't be set as the actual elements value. | |
*/ | |
@Directive({ | |
// tslint:disable-next-line:directive-selector | |
selector: 'input[rxjsInput]' | |
}) | |
export class RxJsInputDirective<T extends string | number | Date> implements OnDestroy { | |
@Input() debounce = 0; | |
private readonly onDestroy$ = new Subject(); | |
private readonly observable: Observable<any>; | |
public readonly el: HTMLInputElement; | |
public readonly type: string; | |
public readonly getMappedValue: () => T; | |
constructor(elRef: ElementRef<HTMLInputElement>) { | |
this.el = elRef.nativeElement; | |
this.type = this.el.getAttribute('type') || 'text'; | |
switch (this.type) { | |
case 'number': this.getMappedValue = () => this.el.valueAsNumber as any; break; | |
case 'date': this.getMappedValue = () => this.el.valueAsDate as any; break; | |
default: this.getMappedValue = () => this.el.value as any; | |
} | |
this.observable = fromEvent(this.el, 'input'); | |
} | |
/** | |
* Returns an observable that emits the input's value. | |
* Automatically maps to correct type based on element's type attribute. | |
* Completes when directive is destroyed. | |
* @param startWithValue Optional value to start the observable with. Doesn't set it as the inputs value | |
*/ | |
asObservable(startWithValue?: T): Observable<T> { | |
const base = this.observable.pipe( | |
takeUntil(this.onDestroy$), | |
debounceTime(this.debounce), | |
map(() => this.getMappedValue()), | |
distinctUntilChanged() | |
); | |
return startWithValue === undefined ? base : base.pipe(startWith(startWithValue)) as any; | |
} | |
ngOnDestroy() { | |
this.onDestroy$.next(); | |
this.onDestroy$.complete(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment