Rails ActionCable消息局部视图头像显示位置错误问题求助
解决ActionCable广播时头像位置错误的问题
我明白你的痛点:现在的逻辑是服务器端用**消息发送者的current_user**渲染好HTML后广播给所有人,导致其他订阅用户拿到的HTML是基于发送者的判断,自然头像位置全错了,而且你不想改动现有局部视图的逻辑。
这里有两种不需要修改_message.html.erb的解决方案,推荐第二种更贴合你的需求:
方案一:客户端渲染(更灵活,适合长期维护)
核心思路是服务器只广播消息的原始数据,让每个客户端用自己本地的current_user来渲染对应的HTML,这样每个用户看到的都是符合自己视角的内容。
步骤1:修改服务器端的create_message方法,广播消息数据而非渲染好的HTML
def create_message(message_params) message = Message.new( context_type: 'Conversation', body: message_params['data']['body'], context_id: message_params['data']['conversation_id'], user_id: message_params['data']['user_id'] ) data = {} if message.save # 广播消息的序列化数据,包含用户信息以便渲染头像 data[:message] = message.as_json(include: { user: { only: [:id, :avatar_url, :name] } }) data[:sender_id] = current_user.id else error_msg = message.errors.full_messages.to_sentence p error_msg data[:error] = error_msg end ActionCable.server.broadcast('room', data) end
步骤2:客户端频道代码中,用本地current_user渲染视图
首先在聊天页面的HTML中提前传递当前用户ID(比如放在页面元素的data属性里):
<!-- 比如在聊天容器上添加 --> <div id="chat-container" data-current-user-id="<%= current_user.id %>"> <!-- 消息列表 --> <div id="messages-list"></div> </div>
然后修改你的MessagesChannel客户端代码(app/javascript/channels/messages_channel.js):
import consumer from "./consumer" consumer.subscriptions.create("MessagesChannel", { received(data) { if (data.error) { alert(data.error); return; } const currentUserId = parseInt(document.getElementById('chat-container').dataset.currentUserId); const message = data.message; // 动态构建HTML(这里直接复用你现有局部视图的结构) let html = ''; if (currentUserId === message.user_id) { html = ` <div class="message right"> <div class="Display Avatar Right"> <img src="${message.user.avatar_url}" alt="${message.user.name}"> </div> <div class="message-body">${message.body}</div> </div> `; } else { html = ` <div class="message left"> <div class="Display Avatar Left"> <img src="${message.user.avatar_url}" alt="${message.user.name}"> </div> <div class="message-body">${message.body}</div> </div> `; } // 添加到消息列表末尾 document.getElementById('messages-list').insertAdjacentHTML('beforeend', html); } });
方案二:服务器端渲染两种版本的HTML(完全复用现有局部视图)
这个方案完全不用改动你的_message.html.erb,服务器端同时渲染发送者视角和接收者视角的HTML,广播时一起发送,客户端根据自己的用户ID选择对应的HTML。
步骤1:修改create_message方法,渲染两种HTML
def create_message(message_params) message = Message.new( context_type: 'Conversation', body: message_params['data']['body'], context_id: message_params['data']['conversation_id'], user_id: message_params['data']['user_id'] ) data = {} if message.save # 1. 渲染发送者视角的HTML(用当前发送者的current_user) sender_html = ApplicationController.render( partial: 'conversations/message', locals: { message: message, current_user: current_user } ) # 2. 渲染接收者视角的HTML(用一个虚拟用户,ID不等于消息发送者ID) receiver_html = ApplicationController.render( partial: 'conversations/message', locals: { message: message, current_user: OpenStruct.new(id: -1) } ) data[:sender_html] = sender_html data[:receiver_html] = receiver_html data[:sender_id] = current_user.id else error_msg = message.errors.full_messages.to_sentence p error_msg data[:error] = error_msg end ActionCable.server.broadcast('room', data) end
步骤2:客户端选择对应的HTML渲染
同样需要在页面上传递当前用户ID,然后修改客户端频道代码:
import consumer from "./consumer" consumer.subscriptions.create("MessagesChannel", { received(data) { if (data.error) { alert(data.error); return; } const currentUserId = parseInt(document.getElementById('chat-container').dataset.currentUserId); // 判断自己是不是发送者,选择对应的HTML const targetHtml = currentUserId === data.sender_id ? data.sender_html : data.receiver_html; // 添加到消息列表 document.getElementById('messages-list').insertAdjacentHTML('beforeend', targetHtml); } });
为什么方案二更适合你?因为它完全保留了你现有_message.html.erb的逻辑,不需要重构前端渲染代码,只需要服务器端多做一次渲染,实现成本很低。
内容的提问来源于stack exchange,提问作者PrimeTimeTran
相关产品推荐
相关产品推荐

