SockJS with Angular does not display the messages

38 Views Asked by At

I am encountering difficulties loading the message view while implementing a chat application with Angular17, simulating WhatsApp, and using spring-boot-starter-websocket in the backend. I've provided the relevant code snippets and repositories below for reference:

  • Spring Boot WebSocket Repository

    github.com/franki-wolf1/Springboot-websocket
    
  • repo angular en

    github.com/franki-wolf1/chat-angular
    
  • whatsapp demo en app-social-post.web.app/chat/


Issue:

The problem arises when opening two chat views, each simulating a different user. Although the messages are successfully reaching the backend, the view does not update to display the messages sent from one user to another.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.chat</groupId>
        <artifactId>springboot</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springboot</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>17</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
                <version>3.2.2</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>

then I leave the controller in the backend

    package com.chat.springboot.controller;
    
    import com.chat.springboot.dto.ChatMessage;
    import org.springframework.messaging.handler.annotation.DestinationVariable;
    import org.springframework.messaging.handler.annotation.MessageMapping;
    import org.springframework.messaging.handler.annotation.SendTo;
    import org.springframework.stereotype.Controller;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    @Controller
    public class WebSocketController {
        @MessageMapping("/chat/{roomId}")
        @SendTo("/topic/{roomId}")
        public ChatMessage chat(@DestinationVariable String roomId, ChatMessage message) {
            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            return new ChatMessage(message.getMessage(), message.getUser(), time);
        }
    }

Next I leave the backend socket configuration

    package com.chat.springboot.configuration;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
    import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
    import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
    import org.springframework.messaging.simp.config.MessageBrokerRegistry;
    
    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
        //AbstractMessageBrokerConfiguration
        @Override
        public void configureMessageBroker(MessageBrokerRegistry registry) {
            System.out.println(registry);
            registry.enableSimpleBroker("/topic");
            registry.setApplicationDestinationPrefixes("/app");
        }
    
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            registry.addEndpoint("/chat-socket");
            registry.addEndpoint("/chat-socket")
                    .setAllowedOrigins("http://localhost:4200")
                    .withSockJS();
        }
    }

Whit the porpertis: server.port=3000


on the frontend, I have these services

    import { Injectable } from '@angular/core';
    import { Stomp } from '@stomp/stompjs';
    import SockJS from 'sockjs-client';
    import { modelChatMessage } from '../models/modelChatMessage';
    import { BehaviorSubject } from 'rxjs';
    
    @Injectable({
      providedIn: 'root'
    })
    export class ChatServices {
    
      private stompCliente: any;
      private messageSubject: BehaviorSubject<modelChatMessage[]> =
      new BehaviorSubject<modelChatMessage[]>([]);
    
    
      constructor() {
        this.initConnectionSocket(); 
      }
    
      initConnectionSocket() {
        const url = '//localhost:3000/chat-socket';
        const socket = new SockJS(url);
        this.stompCliente = Stomp.over(socket);
      }
    
      joinRoom(roomID: any) {
        this.stompCliente.connect({}, () => {
          this.stompCliente.subscribe(`/topic/${roomID}`, (messages: any) => {
            //this.showMessageOutput(JSON.parse(messages.body))
            const messageConten = JSON.parse(messages.body); 
      
            const currentMessages = this.messageSubject.getValue();
            currentMessages.push(messageConten);
      
            this.messageSubject.next([...currentMessages]); // Usar spread operator para mantener la inmutabilidad del array
      });
    });
  } 

  showMessageOutput(messageOutput: any) {
    var response = document.getElementById('response');
    const p = document.createElement('p');
    p.style.wordWrap = 'break-word';
    p.appendChild(document.createTextNode(messageOutput.from + ': '+
    response?.appendChild(p)))

  }

  sendMessage(roomID: string, chatMessage: modelChatMessage) {
    this.stompCliente.send(`/app/chat/${roomID}`, {}, JSON.stringify(chatMessage));
  }

  getMessageSubject() { 
    return this.messageSubject.asObservable();
  }

}

my component is

    import { CommonModule } from '@angular/common';
    import { ChangeDetectionStrategy, Component } from '@angular/core';
    import { ChatServices } from '../../services/chat.service';
    import { ActivatedRoute } from '@angular/router';
    import { modelChatMessage } from '../../models/modelChatMessage';
    import { FormsModule } from '@angular/forms';
    
    @Component({
      selector: 'app-chat',
      standalone: true,
      imports: [
        CommonModule, FormsModule
      ],
      templateUrl: './chat.component.html',
      styleUrl: './chat.component.scss',
      changeDetection: ChangeDetectionStrategy.OnPush,
    })
    export default class ChatComponent {
      messageInput: string = '';
      userId: string="";
      messageList: any[] = [{
        message: 'hola',
        user: '',
        state: '',
      }];
    
      constructor(private chatServices: ChatServices,
        private route: ActivatedRoute
        ){ 
      }
    
      ngOnInit(): void {
        this.userId = this.route.snapshot.params["userId"];
        this.chatServices.joinRoom(this.userId);
        this.listenerMessage();
      }
    
      sendMessage() { 
        console.log('22222222::::::::::_'+this.userId);
        
        const chatMessage = {
          message: this.messageInput,
          user: this.userId,
          state: this.userId + ' send'
        }as modelChatMessage
    
        this.chatServices.sendMessage(this.userId, chatMessage);
        this.messageInput = '';  
      }
     
      listenerMessage() {
        this.chatServices.getMessageSubject().subscribe((messages: any) => {
          this.messageList = messages.map((item: any)=> ({
            ...item,
            message_side: item.user === this.userId ? 'sender': 'receiver'
          }))
        });
      }
    }

Ans the view is:

    <div class="chat_window">
      <div class="title d-flex align-items-center">
        <img src="https://png.pngtree.com/png-vector/20221018/ourmid/pngtree-whatsapp-phone-icon-png-image_6315989.png" width="45px" height="45px">
        <h3 class="wsp" (click)="listenerMessage()">WhatsApp</h3>
      </div> 
      <div class="top_menu">
        <div class="buttons">
          <div class="button close"></div>
          <div class="button minimize"></div>
          <div class="button maximize"></div>
        </div>
      </div>
    
      <ul class="messages">
        <li class="message right" *ngFor="let item of messageList"
        [ngClass]="{'left': item.message_side === 'receiver', 'right': item.message_side === 'sender'}">
          <div class="avatar"></div>
          <div class="text_wrapper">
            <div class="text">{{ item.message }}</div>
          </div>
        </li>
      </ul>
      
      
      <div class="bottom_wrapper clearfix">
        <div class="message_input_wrapper">
          <!-- Corregir el binding ngModel y agregar el atributo name -->
          <input class="message_input" placeholder="Message..." [(ngModel)]="messageInput" name="messageInput" />
        </div>
        <div class="send_message" (click)="sendMessage()">
          <div class="icon"></div>
          <div class="text">Send</div>
        </div>
      </div>
    </div>

Additional Information:

  • Backend server is configured with server.port=3000.
0

There are 0 best solutions below