Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save pahlers/a9bbba3a0c40bdfa5d2ac7bd973f1912 to your computer and use it in GitHub Desktop.
Save pahlers/a9bbba3a0c40bdfa5d2ac7bd973f1912 to your computer and use it in GitHub Desktop.
import { HostListener } from '@angular/core';
import { Subject, UnaryFunction, Observable } from 'rxjs';
export function ObservableHostListener<T, R = never>(
eventName: string,
args?: string[],
operator?: UnaryFunction<Observable<T>, Observable<R>>
): PropertyDecorator {
// Calling HostListener will return a decorator, ready to
// be used by TypeScript.
const preparedDecorator: (target: any, key: string) => void = HostListener(eventName, args);
// Returning a PropertyDecorator here, in which preparedDecorator
// will have to be called to register the event name with Angular's
// change detection mechanism.
return (target: any, key: string) => {
// Creating an internal subject that will recieve values from
// Angular calling the shadowed method with new values.
const subject = new Subject<T>();
// We need a super secret string to monkey patch a new method onto
// the target class.
const newKey = `__keyForObservableHostListener__${key}`;
Object.defineProperties(target, {
[newKey]: {
// here, we just next() the recieved values into the internal subject.
value: (val: T) => subject.next(val),
configurable: false,
enumerable: false,
writable: false
},
// And here, we're assigning our internal to the original key.
[key]: {
get() {
if (operator) {
return subject.pipe(operator);
} else {
return subject.asObservable();
}
}
}
});
// Patch ngOnDestroy so we can correctly complete our subject.
patchOnDestroy(target, subject);
// Call our preparedDecorator with the target class and our super secret key,
// so Angular wires the event to our monkey patched method instead of our
// decorated key.
preparedDecorator(target, newKey);
}
}
function patchOnDestroy(target: any, subject: Subject<any>) {
const oldOnDestroy = target['ngOnDestroy'] || (() => undefined);
target['ngOnDestroy'] = () => {
subject.complete();
oldOnDestroy();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment