'Angular decorator call without trigger preserve context
I'm trying to create a decorator for an angular component function, so my web sockets are automatically setup in an easy an conform way.
I currently store the context, but I feel like there is a better way, calling it without the context, gives undefined errors.
decorator
import { SocketService } from './socket.service';
export function Listen(topic: string, options?: DecoratorOptions) {
return function (target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
// Original context
let self: unknown;
// default values for our config, we’ll overwrite this with our options parameter
const config = {
path: '/',
} as DecoratorOptions;
// overwrite any keys passed in to our decorator in the config object
if (options) {
Object.keys(options).forEach((x) => (config[x] = options[x]));
}
// Subscribe to the topic
const service = SocketService.getService({ path: config.path });
let subscription = service.fromEvent(topic).subscribe(function (args) {
descriptor.value.call(self, args);
});
// Destroy subscription on component destroy
const _originalOnDestroy = target['ngOnDestroy'];
target['ngOnDestroy'] = function () {
console.log('destroy');
subscription.unsubscribe();
if (_originalOnDestroy) {
_originalOnDestroy?.apply(this);
}
};
// Store original context
const _originalOnInit = target['ngOnInit'];
target['ngOnInit'] = function () {
self = this;
if (_originalOnInit) {
_originalOnInit?.apply(this);
}
};
return descriptor;
};
}
export interface DecoratorOptions {
path?: string;
}
Component
@Listen(EVENTS.JOB_STARTED)
jobStarted(data: Cron) {
const index = this.dataSource.data.findIndex((cron) => cron.id === data.id);
this.dataSource.data[index]!.running! = true;
}
I know its already solved, but I think it would be helpful for myself and others to know if there is a better way.
Solution 1:[1]
Update: found it. but maybe there is a better way.
I now setup the subscription in the ngOnInit instead of the function. This gives the intended result
import { Subscription } from 'rxjs';
import { AppInjector } from './socket.module';
import { SocketService } from './socket.service';
export function ListenTopic(topic: string, options?: DecoratorOptions) {
return function (target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
// default values for our config, we’ll overwrite this with our options parameter
const config = {
path: '/',
} as DecoratorOptions;
// overwrite any keys passed in to our decorator in the config object
if (options) {
Object.keys(options).forEach((x) => (config[x] = options[x]));
}
// Create subscription object
let subscription: Subscription;
// Destroy subscription on component destroy
const _originalOnDestroy = target['ngOnDestroy'];
target['ngOnDestroy'] = function () {
subscription?.unsubscribe();
if (_originalOnDestroy) {
_originalOnDestroy?.apply(this);
}
};
// Store original context
const _originalOnInit = target['ngOnInit'];
target['ngOnInit'] = function () {
// Get the socket service
const service = AppInjector.get(SocketService)?.getService({ path: config.path });
// Subscribe to the topic
subscription = service.fromEvent(topic).subscribe((args) => {
descriptor.value.call(this, args);
});
if (_originalOnInit) {
_originalOnInit?.apply(this);
}
};
return descriptor;
};
}
export interface DecoratorOptions {
path?: string;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Kiwi |
