You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular12与Node.js中基于socket id区分收发消息并解决重复展示问题

问题修复方案

1. 消息重复展示问题修复

出现该问题有两个核心原因:

  • 模板中循环消息时,同时渲染了两个消息容器,没有加判断条件,导致每条消息都会在两个容器中各显示一次
  • 聊天服务中每次调用getNewMessage方法都会新增一次socket消息监听,多次调用就会重复接收同一条消息

对应修改步骤:

第一步:修改Node.js后端代码

调整消息返回结构,携带发送者的socket id,方便前端区分消息发送方:

const app = require('express')();
const httpServer = require('http').createServer(app);
const io = require('socket.io')(httpServer, {
  cors: {origin : '*'}
});

const port = process.env.PORT || 3000;

io.on('connection', (socket) => {
  console.log('a user connected');

  socket.on('message', (message) => {
    // 返回结构化消息,包含发送者id和消息内容
    io.emit('message', {
      senderId: socket.id,
      content: message
    });
  });

  socket.on('disconnect', () => {
    console.log('a user disconnected!');
  });
});

httpServer.listen(port, () => 
  console.log(`listening on port ${port}`));
第二步:修改chat.service.ts代码

将socket消息监听移到构造函数仅注册一次,同时存储当前用户的socket id用于后续判断:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { io } from "socket.io-client";

@Injectable({
 providedIn: 'root'
})
export class ChatService {
  // 调整消息类型为结构化对象
  public message$: BehaviorSubject<{senderId: string, content: string}> = new BehaviorSubject(null);
  // 存储当前用户的socket id
  public mySocketId: string = '';
  socket = io('http://localhost:3000');

  constructor() {
    // 连接成功后获取当前用户的socket id
    this.socket.on('connect', () => {
      this.mySocketId = this.socket.id;
    });
    // 全局仅注册一次消息监听
    this.socket.on('message', (message) =>{
      this.message$.next(message);
    });
  }

  public sendMessage(message:any) {
    this.socket.emit('message', message);
  }

  public getNewMessage = () => {
    return this.message$.asObservable();
  };
}
第三步:修改app.component.ts代码

调整消息列表类型,过滤空消息避免多余渲染:

import { Component } from '@angular/core';
import { ChatService } from './chat.service';
import * as moment from 'moment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'AngularApp';
  // 将chatService改为public,方便模板中访问
  constructor(public chatService: ChatService){}

  newMessage!: string;
  // 调整消息列表为结构化对象数组
  messageList: {senderId: string, content: string}[] = [];

  ngOnInit(){
    this.chatService.getNewMessage().subscribe((message) => {
      // 过滤掉BehaviorSubject默认的空值
      if(message) {
        let currentTime = moment().format('hh:mm:ss a');
        this.messageList.push(message);
      }
    })
  }

  sendMessage() {
    this.chatService.sendMessage(this.newMessage);
    this.newMessage = '';
  }
}
第四步:修改app.component.html代码

增加发送方判断逻辑,同一条消息仅渲染对应侧的容器:

<div class="container">
  <div class="chat-box">
    <!-- client -->
    <div class="client">
      <h2>Simple Chat App</h2>
    </div>
    <!-- Main Chat Section -->
    <div class="chats" *ngFor="let message of messageList">
      <!-- 对方消息显示在左侧 -->
      <div *ngIf="message.senderId !== chatService.mySocketId" class="client-chat">
        {{message.senderId.substr(0,2)}} said--{{message.content}}
      </div>
      <!-- 自己的消息显示在右侧 -->
      <div *ngIf="message.senderId === chatService.mySocketId" class="my-chat">
        {{message.content}}
      </div>
    </div>
    <!-- Input Field -->
    <div class="chat-input">
      <input type="text"  [(ngModel)]="newMessage" (keyup)="$event.keyCode == 13 && sendMessage()" placeholder="Enter Message">
      <button (click)="sendMessage()" id="send-btn" class="btn btn-info">
        Send
      </button>
    </div>
  </div>
</div>

2. 收发消息分两侧展示实现

上述修改已经同步实现了该需求:

  • 对方发送的消息会匹配client-chat样式,显示在界面左侧,带发送者id前缀
  • 自己发送的消息会匹配my-chat样式,显示在界面右侧
  • 原有样式不需要做任何调整即可生效

内容的提问来源于stack exchange,提问作者Amir Shahzad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 03:54:06