0

I'm trying to create a very simple chat application in angular using socket io. I short I have a component that views the chat and service that listen to incoming messages and adds them to the component object to be viewed.

My problem is that I'm passing the conversation object(a reference to the listener so that It can append the message to the object), however, the new message does not appear in the chat component.

Should I change the object into a behavior subject since I reckon the problem is with updating the view?

Please find the code below:

Conversation Service:

  listenToIncomingFromAllConversations(conversations: Conversation[]) {
this.webSocketService.listen(this.authService.currentUserValue.id).subscribe(data => {
  console.log("The message recieved from the server is:")
  const message = data as Message;
  var conversation: Conversation = null;
  console.log(message);

  this.getUserConversations().subscribe(convos => {
    /** Refresh conversation and decorate them */
    conversations = convos;
    this.decorateMessageTabConvos(conversations, convos);
    conversation = conversations.find(convo => convo._id == message.conversationID);
    console.log("The conversation appear and ready to be append by the new message!");
    console.log(conversation);
    conversation.otherMemberID = this.getOtherMember(conversation.members);
    conversation.messages.push(message);
    this.decorateChatBoxMessages(conversation);
  });
});

}

Chat Component:

  ngOnInit(): void {
    this.getConversaitonDecorated();
    this.convoService.listenToIncomingFromAllConversations(this.conversations);
  }

  getConversaitonDecorated() {
    this.convoService.getUserConversations().subscribe(convos => {
      this.convoService.decorateMessageTabConvos(this.conversations, convos);
    });
  }

As you can see the listener listens for messages from the server and is initialized in the lifecycle hook of the chat application and the conversation object (the holds all the user's conversation) is passed to the listener for the message to be appended to it.

The message gets appended since it is passed by reference and appears in the console log. However, when I view the specific application that the message was appended to, it is not visible in the view.

Thanks

Omar Jarkas
  • 100
  • 5

1 Answers1

0

I think that the problem you have is linked to Angular Change Detection not being triggered by the WebSocket message events.

Angular is not aware of what happens in the browser, and uses a library called Zone.JS, that patches every single native object in the browser (XmlHttpRequest, fetch, settimeout, setinterval, etc.) in order to trigger a change detection every time something happens.

AFAICT Zone.JS doesn't trigger properly every WebSocket method (or the library you are using are loaded before ZoneJS and therefore are invisible to it).

A simple solution is in modifying the WebSocketService, if it's a class under your control, and change the code that publishes the messages to use ngZone.run:

class WebSocketService {
  private messages: Subject<Message>;

  constructor(private zone: NgZone) {
    ...

    // in your code you will have something like this:
    socket.addEventListener('message', (event) => {
      // this line runs outside of the Angular zone, so we need:
        this.zone.run(() => {
          // this code is inside "the Zone", so it will trigger change detection
          this.receiveMessage(event);
        });
      });
    }

  receiveMessage(message: MessageEvent) {
    // do your processing
  }
}

Useful links:

NgZone official docs
Some information about disabling patching of WebSocket entirely
This question is about accessing native versions of some methods, but in my answer there is a sample about how to disable patching of WebSocket prototype.

Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53