Rails 5 ActionCable 使用 DIV 工作,但不使用 UL
Rails 5 ActionCable Works using DIV, but not UL
我使用 Rails 5 ActionCable 创建了一个 WebSocket 消息传递功能。当我使用 DIV 作为容器时效果很好。但是当我将其更改为使用 UL 和 LI 时;它停止工作了。我似乎无法调试它;但看起来 received:
事件没有在客户端触发。 send_message 有效...并保存在数据库中,它只是新消息不会出现在页面上。
TL;DR;位在问题的底部。
更新
我已经设法诊断出问题,当我删除检查 current_user message.user == current_user
的行(从 _message.html.erb 部分)时它再次工作- 但为什么呢?
将不胜感激。这是(我认为)所有相关代码:
/app/assets/javascripts/channels/conversations.咖啡
jQuery(document).on 'turbolinks:load', ->
messages = $('#messages')
if $('#messages').length > 0
messages_to_bottom = -> messages.scrollTop(messages.prop("scrollHeight"))
messages_to_bottom()
App.global_chat = App.cable.subscriptions.create {
channel: "ConversationsChannel"
conversation_id: messages.data('conversation-id')
},
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
messages.append data['message']
messages_to_bottom()
send_message: (message, conversation_id) ->
@perform 'send_message', message: message, conversation_id: conversation_id
$('#new_message').submit (e) ->
$this = $(this)
textarea = $this.find('#message_body')
if $.trim(textarea.val()).length > 1
App.global_chat.send_message textarea.val(), messages.data('conversation-id')
textarea.val('')
e.preventDefault()
return false
/app/channels/conversations_channel.rb
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
class ConversationsChannel < ApplicationCable::Channel
def subscribed
stream_from "conversations_#{params['conversation_id']}_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def send_message(data)
current_user.messages.create!(body: data['message'], conversation_id: data['conversation_id'], user: current_user)
end
end
/app/jobs/message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
ActionCable.server.broadcast "conversations_#{message.conversation.id}_channel",
message: render_message(message)
end
private
def render_message(message)
MessagesController.render partial: 'messages/message', locals: {message: message}
end
end
/app/views/conversations/show.html.erb
...
<div id="messages" data-conversation-id="<%= @conversation.id %>">
<ul class="list-unstyled media-block messages-list" data-conversation-id="<%= @conversation.id %>">
<%= render @conversation.messages %>
</ul>
</div>
...
<div class="row">
<%= form_for @message, url: '#' do |f| %>
<%= hidden_field_tag 'conversation_id', @conversation.id %>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, class: 'form-control' %>
<small class="text-muted">From 2 to 1000 characters</small>
</div>
<%= f.submit "Post", class: 'btn btn-primary btn-lg' %>
<% end %>
</div>
...
/app/views/messages/_message.html.erb
<% if message.body %>
<li class="mar-btm message" data-id="<%= message.id %>">
<div class="<%= message.user == current_user ? 'media-right' : 'media-left' %>">
<img src="<%= message.user.avatar.url %>" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor
<% if message.user == current_user %>speech-right
<% end %>">
<div class="speech">
<h4><%= message.user.full_name %></h4>
<%= message.body %>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i>
<span class="timestamp" data-value="<%= message.created_at %>"><%= time_ago_in_words message.created_at %>
ago</span>
</p>
</div>
</div>
</li>
<% end %>
TL;DR;
正如我所说,之前我使用的是容器 div: <div id="messages" data-conversation-id="<%= @conversation.id %>">
并且每条消息都在子 DIV 中而不是 LI 元素中,并且它正在工作。我不知道我弄坏了什么(或者为什么它很重要)。
当我 post 发送新消息并保存到数据库时,它不会发送到浏览器。日志看起来像这样:
[ActionCable] [an...ey@gmail.com] ConversationsChannel#send_message({"message"=>"23r23r2", "conversation_id"=>5})
[ActionCable] [an...ey@gmail.com] (0.1ms) BEGIN
[ActionCable] [an...ey@gmail.com] SQL (1.1ms) INSERT INTO "messages" ("conversation_id", "user_id", "body", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id" [["conversation_id", 5], ["user_id", 7], ["body", "23r23r2"], ["created_at", 2017-03-01 01:16:25 UTC], ["updated_at", 2017-03-01 01:16:25 UTC]]
[ActionCable] [an...ey@gmail.com] (0.9ms) COMMIT
[ActionCable] [an...ey@gmail.com] [ActiveJob] Enqueued MessageBroadcastJob (Job ID: a464e290-f23f-4cd4-a355-e364a43782e9) to Async(default) with arguments: #<GlobalID:0x007ff678cf74e0 @uri=#<URI::GID gid://itpo/Message/172>>
Message Load (0.6ms) SELECT "messages".* FROM "messages" WHERE "messages"."id" = LIMIT [["id", 172], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Performing MessageBroadcastJob from Async(default) with arguments: #<GlobalID:0x007ff679dcc400 @uri=#<URI::GID gid://itpo/Message/172>>
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Conversation Load (0.3ms) SELECT "conversations".* FROM "conversations" WHERE "conversations"."id" = LIMIT [["id", 5], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = LIMIT [["id", 7], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Rendered messages/_message.html.erb (16.6ms)
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Performed MessageBroadcastJob from Async(default) in 28.37ms
并且由于日志的最后一行显示 Performed MessageBroadcastJob from Async(default)
这让我相信服务器端不存在问题,并且 可能我的咖啡脚本不知何故被破坏了。
也许将新元素插入 UL 的方式与仅将 HTML 附加到 DIV 不同?
请帮忙。
问题与 _message.html.erb 部分视图有关,它正在对 Devise 的 'current_user' 进行比较。我想无状态的 WebSocket 对用户会话数据的访问范围与视图在常规条件下所具有的访问范围不同。作为补偿,我在相关的 DOM 元素中添加了数据属性,并创建了一个特殊的 JavaScript 函数来应用差异化样式并将数据转换为所需的方式。它现在似乎完美无缺。
我使用 Rails 5 ActionCable 创建了一个 WebSocket 消息传递功能。当我使用 DIV 作为容器时效果很好。但是当我将其更改为使用 UL 和 LI 时;它停止工作了。我似乎无法调试它;但看起来 received:
事件没有在客户端触发。 send_message 有效...并保存在数据库中,它只是新消息不会出现在页面上。
TL;DR;位在问题的底部。
更新
我已经设法诊断出问题,当我删除检查 current_user message.user == current_user
的行(从 _message.html.erb 部分)时它再次工作- 但为什么呢?
将不胜感激。这是(我认为)所有相关代码:
/app/assets/javascripts/channels/conversations.咖啡
jQuery(document).on 'turbolinks:load', ->
messages = $('#messages')
if $('#messages').length > 0
messages_to_bottom = -> messages.scrollTop(messages.prop("scrollHeight"))
messages_to_bottom()
App.global_chat = App.cable.subscriptions.create {
channel: "ConversationsChannel"
conversation_id: messages.data('conversation-id')
},
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
messages.append data['message']
messages_to_bottom()
send_message: (message, conversation_id) ->
@perform 'send_message', message: message, conversation_id: conversation_id
$('#new_message').submit (e) ->
$this = $(this)
textarea = $this.find('#message_body')
if $.trim(textarea.val()).length > 1
App.global_chat.send_message textarea.val(), messages.data('conversation-id')
textarea.val('')
e.preventDefault()
return false
/app/channels/conversations_channel.rb
# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading.
class ConversationsChannel < ApplicationCable::Channel
def subscribed
stream_from "conversations_#{params['conversation_id']}_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def send_message(data)
current_user.messages.create!(body: data['message'], conversation_id: data['conversation_id'], user: current_user)
end
end
/app/jobs/message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
ActionCable.server.broadcast "conversations_#{message.conversation.id}_channel",
message: render_message(message)
end
private
def render_message(message)
MessagesController.render partial: 'messages/message', locals: {message: message}
end
end
/app/views/conversations/show.html.erb
...
<div id="messages" data-conversation-id="<%= @conversation.id %>">
<ul class="list-unstyled media-block messages-list" data-conversation-id="<%= @conversation.id %>">
<%= render @conversation.messages %>
</ul>
</div>
...
<div class="row">
<%= form_for @message, url: '#' do |f| %>
<%= hidden_field_tag 'conversation_id', @conversation.id %>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, class: 'form-control' %>
<small class="text-muted">From 2 to 1000 characters</small>
</div>
<%= f.submit "Post", class: 'btn btn-primary btn-lg' %>
<% end %>
</div>
...
/app/views/messages/_message.html.erb
<% if message.body %>
<li class="mar-btm message" data-id="<%= message.id %>">
<div class="<%= message.user == current_user ? 'media-right' : 'media-left' %>">
<img src="<%= message.user.avatar.url %>" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor
<% if message.user == current_user %>speech-right
<% end %>">
<div class="speech">
<h4><%= message.user.full_name %></h4>
<%= message.body %>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i>
<span class="timestamp" data-value="<%= message.created_at %>"><%= time_ago_in_words message.created_at %>
ago</span>
</p>
</div>
</div>
</li>
<% end %>
TL;DR;
正如我所说,之前我使用的是容器 div: <div id="messages" data-conversation-id="<%= @conversation.id %>">
并且每条消息都在子 DIV 中而不是 LI 元素中,并且它正在工作。我不知道我弄坏了什么(或者为什么它很重要)。
当我 post 发送新消息并保存到数据库时,它不会发送到浏览器。日志看起来像这样:
[ActionCable] [an...ey@gmail.com] ConversationsChannel#send_message({"message"=>"23r23r2", "conversation_id"=>5})
[ActionCable] [an...ey@gmail.com] (0.1ms) BEGIN
[ActionCable] [an...ey@gmail.com] SQL (1.1ms) INSERT INTO "messages" ("conversation_id", "user_id", "body", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id" [["conversation_id", 5], ["user_id", 7], ["body", "23r23r2"], ["created_at", 2017-03-01 01:16:25 UTC], ["updated_at", 2017-03-01 01:16:25 UTC]]
[ActionCable] [an...ey@gmail.com] (0.9ms) COMMIT
[ActionCable] [an...ey@gmail.com] [ActiveJob] Enqueued MessageBroadcastJob (Job ID: a464e290-f23f-4cd4-a355-e364a43782e9) to Async(default) with arguments: #<GlobalID:0x007ff678cf74e0 @uri=#<URI::GID gid://itpo/Message/172>>
Message Load (0.6ms) SELECT "messages".* FROM "messages" WHERE "messages"."id" = LIMIT [["id", 172], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Performing MessageBroadcastJob from Async(default) with arguments: #<GlobalID:0x007ff679dcc400 @uri=#<URI::GID gid://itpo/Message/172>>
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Conversation Load (0.3ms) SELECT "conversations".* FROM "conversations" WHERE "conversations"."id" = LIMIT [["id", 5], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = LIMIT [["id", 7], ["LIMIT", 1]]
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Rendered messages/_message.html.erb (16.6ms)
[ActiveJob] [MessageBroadcastJob] [a464e290-f23f-4cd4-a355-e364a43782e9] Performed MessageBroadcastJob from Async(default) in 28.37ms
并且由于日志的最后一行显示 Performed MessageBroadcastJob from Async(default)
这让我相信服务器端不存在问题,并且 可能我的咖啡脚本不知何故被破坏了。
也许将新元素插入 UL 的方式与仅将 HTML 附加到 DIV 不同?
请帮忙。
问题与 _message.html.erb 部分视图有关,它正在对 Devise 的 'current_user' 进行比较。我想无状态的 WebSocket 对用户会话数据的访问范围与视图在常规条件下所具有的访问范围不同。作为补偿,我在相关的 DOM 元素中添加了数据属性,并创建了一个特殊的 JavaScript 函数来应用差异化样式并将数据转换为所需的方式。它现在似乎完美无缺。