Angular websocket 事件装饰器

分类:JavaScript     发布时间:2021-01-06     最后更新:2021-01-06     浏览数:2505
一个站在巨人肩膀上的 Angular websocket 事件装饰器

本文假设你已经看过并熟悉了参考文章

其他和参考文章的都一样,我就不赘述了。建议先看参考文章。

我遇到的问题是,在两个及以上组件使用装饰器会出现问题,具体就是找不到属性或方法。

使用

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,第二个是函数

解决方法

  1. 改装饰器

    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;
    };
    }
  2. 改管事件理器

    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();
    }
    }

以上的解决方法虽不是很优雅,读起来也有点苦涩,但好在能解决我的问题,如果有更好的解决方法,欢迎回复

参考

使用 Typescript 构建类型安全的 Websocket 应用

上一篇: 按楼层名称排序 下一篇: 使用 Angular pipe 过滤、排序、分页