我正在 Rails 中构建聊天。我有一个用于发送消息的 MessagesController 和一个用于通过 websocket 接收消息的通道(操作电缆)。
这是我的 MessagesController#create:
def create
message = Message.create!(message_params)
ActionCable.server.broadcast(
"some_channel",
render(message) # app/views/messages/_message.html.erb
)
end
接收客户端然后获取渲染的 HTML 并将其附加到页面:
<div class="border-bottom p-3">
<h6><strong><%= message.from %></strong></h6>
<div class="message-body">
<%= markdown message.body %>
</div>
</div>
使用
render @messages
加载页面时,使用相同的模板呈现所有消息。
在发送者端,我打算在消息表单上使用
remote: true
,然后在 MessagesController#create 底部渲染 JS 来运行 JS 回调。我觉得这是 Rails 的做事方式。但是,正如你所看到的,我已经在控制器中调用了 render
,并且 Rails 不允许我调用 render 两次。
我想我可以自己评估消息模板,以避免第一次使用 ERB 调用
render
:
def create
message = Message.create!(message_params)
message_template = File.read('app/views/messages/_message.html.erb')
ActionCable.server.broadcast(
"conversations:#{params[:conversation_id]}",
ERB.new(message_template).result(binding)
)
render 'create', handlers: ['js.erb']
end
但是使用 ERB 构建模板失败,因为控制器无法识别我在消息模板中调用的辅助方法 (
markdown
)。我尝试将方法从视图上下文导入到控制器上下文中,但它不起作用。也许我可以将不同的绑定传递给 ERB,但我不知道哪个(它不接受 view_context
,即使接受,变量 message
在其中也不可用)。
所以现在,我只能手动编写 ajax 请求,而不是在表单上使用
remote: true
。我怀疑我可以使用涡轮流来完成这类事情(我以前从未使用过),但仅仅为此安装涡轮似乎有点矫枉过正。我无法摆脱这样的感觉:必须有一种简单的 Rails 方式来完成我想做的事情。
如何绕过 Rails 不允许双重渲染的限制?或者,我愿意接受另一种方法。
编辑:我想到我可以通过套接字发送新消息,而不是使用 MessagesController#create。由于我是从 JS 执行此操作,因此我可以在那里运行我的“回调”。但我使用套接字这一事实是偶然的;我可以看到其他场景,其中需要绕过不涉及套接字的双重渲染。
编辑 2:另一个想法:我可以仅使用控制器来保存和渲染消息,然后从客户端广播渲染的消息。但随后我需要一个系统来识别和忽略往返(自己发送的传入消息)——对于如此简单的事情来说,这似乎也有些过分了。再说一遍,套接字是一个偶然的实现细节。