Created
November 9, 2018 14:05
-
-
Save WouterSpaak/0e1ade13809805d9d0ede2f080876e54 to your computer and use it in GitHub Desktop.
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 { 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