本文假设你已经看过并熟悉了参考文章
其他和参考文章的都一样,我就不赘述了。建议先看参考文章。
我遇到的问题是,在两个及以上组件使用装饰器会出现问题,具体就是找不到属性或方法。
使用
export class ChatPanelComponent extends MessageListenersManager {
constructor(wsMessageService: WsMessageService) {
super(wsMessageService);
}
@MessageListener(Receive.SingleChatMessage)
onSingleChatMessageRead(message: SingleChatMessage) {
console.log('[ChatPanelComponent][onSingleChatMessageRead]: ', message);
this.pushMessageToChatPanel(message);
}
}
出错信息如下
core.js:15724 ERROR TypeError: this.pushMessageToChatPanel is not a function
at ClientComponent.push../src/client/shared/components/chat-panel/chat-panel.component.ts.ChatPanelComponent.onTaskChatMessage (chat-panel.component.ts:132)
at SafeSubscriber._next (message-listener.ts:29)
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:194)
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:132)
at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:76)
at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:53)
at TakeUntilSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:76)
at TakeUntilSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:53)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:41)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:53)
pushMessageToChatPanel
在组件 chat-panel.component.ts
上是存在 ,很显然是 this
的指向问题。
既然知道是 this
指向的问题,那么就让 this
指向就好了。
思路:在装饰器中把一个数组压入 __messageListeners__
,数组第一个元素是组件的 constructor
,第二个是函数
解决方法
-
改装饰器
import { MessageReceiveData } from '@client/core/models/ws-message.model'; import { takeUntil } from 'rxjs/operators'; import { MessageListenersManager } from './message-listeners-manager'; export type ReceiveArgumentsType<T extends keyof MessageReceiveData> = MessageReceiveData[T] extends undefined ? () => void : (data?: MessageReceiveData[T]) => void; export function MessageListener<T extends keyof MessageReceiveData>(type: T) { return ( target: MessageListenersManager, propertyKey: string, descriptor: TypedPropertyDescriptor<ReceiveArgumentsType<T>>, ) => { // 获取构造上的静态属性 __messageListeners__ const constructor = Object.getPrototypeOf(target).constructor; if (constructor && constructor.__messageListeners__) { // 将创建订阅的方法推入 __messageListeners__ , 以便在构造时调用 constructor.__messageListeners__.push([target.constructor, function () { // 创建指定类型的订阅 this.wsMessageService .receive(type) // 使用 takeUntil 操作符以便自动取消订阅 .pipe(takeUntil(this.__messageListenersTakeUntilDestroy$__)) .subscribe((data) => { // 收到返回后调用被装饰得方法 descriptor.value.call(this, data); }); }]); } return descriptor; }; }
-
改管事件理器
import { OnDestroy } from '@angular/core'; import { WsMessageService } from '@client/core'; import { Subject } from 'rxjs'; export class MessageListenersManager implements OnDestroy { static __messageListeners__: [any, Function][] = []; readonly __messageListenersTakeUntilDestroy$__ = new Subject<void>(); constructor(public wsMessageService: WsMessageService) { MessageListenersManager.__messageListeners__.forEach((item) => { const [ctr, fun] = item; if (this instanceof ctr) { const index = MessageListenersManager.__messageListeners__.findIndex(a => a == item); if (index > -1) { // console.log(`${index} matched:, ${key.name}`); fun.apply(this); // MessageListenersManager.__messageListeners__.splice(index, 1); } } }); } ngOnDestroy(): void { this.__messageListenersTakeUntilDestroy$__.next(); this.__messageListenersTakeUntilDestroy$__.complete(); } }
以上的解决方法虽不是很优雅,读起来也有点苦涩,但好在能解决我的问题,如果有更好的解决方法,欢迎回复。