Rails 如何使用 ActionCable 在 "received:" 上呈现全新页面
Rails How to use ActionCable to render whole new page on "received:"
我正在尝试制作一个具有 2 种用户类型和此用例的测验应用程序(非单页应用程序):
- 测验主持人用户和参与者用户正在“等待
页”直到测验开始
- 测验主持人用户点击 "quiz start" 按钮开始测验
- 这会立即强制所有参与者用户重定向到带有测验问题的新页面
我按照教程在我的应用程序中设置了 ActionCable,但我想知道的是如何实现第 3 步。我当前的频道 coffeescript 文件如下所示:
# \app\assets\javascripts\channels\quiz_data.coffee:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
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) ->
# Called when there's incoming data on the websocket for this channel
if current_student
# HERE I want to render the page "quiz_question.html.erb"
# ...and then I want to append the answer buttons:
$('answer-buttons').append(data.partial)
send_data: ->
@perform 'send_data'
我很确定答案很简单,但我在谷歌上搜索了很多 Coffeescript 和 ActionCable 教程,几乎所有教程都只呈现用户已经访问的页面的部分内容。我是 Rails 初学者,对 Coffeescript 一无所知,所以任何帮助将不胜感激!
编辑:
这是我的 coffeescript 文件在尝试遵循莱思的回答后的样子:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
connected: ->
# Called when the subscription is ready for use on the server
$('#btn btn-primary btn-lg').on 'click', (e) -> App.quiz_data.send_data()
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
# Called when there's incoming data on the websocket for this channel
if current_student
pageHtml = data.page_html
answerHtml = data.answer_html
# This will replace your body with the page html string:
$('body').html(pageHtml)
# Then add your answer buttons:
#$('#answer-buttons').html(answerHtml)
$('answer-buttons').append(answerHtml)
send_data: ->
@perform 'send_data'
这是我创建的用于渲染我的部分并广播它们的作业:
# app\assets\jobs\quiz_data_broadcast_job.rb:
class QuizDataBroadcastJob < ApplicationJob
queue_as :default
def perform(answers)
ActionCable.server.broadcast('quiz_data', {
page_html: render_page,
answer_html: render_answer_options(answers)
})
end
private
def render_page
ApplicationController.render(
partial: 'pages/student/quiz_question',
locals: {}
)
end
def render_answer_options(answers)
answers.each do |answer|
ApplicationController.render(
#render student/quiz_question page and render as many answer_option partials as needed
partial: 'pages/student/answer_option',
locals: {answer: answer}
)
end
end
end
编辑 2:
这是我的 Javascript 控制台所说的:
Uncaught ReferenceError: App is not defined
at quiz_data.self-21fd077347e9c34e83bab1a2d43a8db5b083fff7ed4eaa02e5314aa78f1dba8b.js:2
at quiz_data.self-21fd077347e9c34e83bab1a2d43a8db5b083fff7ed4eaa02e5314aa78f1dba8b.js:23
这就是我点击它时显示的内容:
1 (function() {
2 App.quiz_data = App.cable.subscriptions.create("QuizDataChannel", {
3 connected: function() {
4 return $('#btn btn-primary btn-lg').on('click', function(e) {
5 return App.quiz_data.send_data();
6 });
7 },
8 disconnected: function() {},
9 received: function(data) {
10 var answerHtml, pageHtml;
11 if (current_student) {
12 pageHtml = data.page_html;
13 answerHtml = data.answer_html;
14 $('body').html(pageHtml);
15 return $('answer-buttons').append(answerHtml);
16 }
17 },
18 send_data: function() {
19 return this.perform('send_data');
20 }
21 });
22
23 }).call(this);
编辑 3:
这是我的 cable.js:
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the rails generate channel command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
WebSockets 并非真正设计用于执行 HTTP 重定向。但是,你仍然可以通过一些jQuery.
给人一种页面发生变化的感觉
由于您将在主持人点击开始后同时创建问题和答案,因此您可以将它们都发送到广播中。
我首先建议将 quiz_question.html.erb
文件更改为部分文件:_quiz_question.html.erb
,这样您就可以生成 HTML 并将其附加到您的正文中。接下来,将 answer-buttons
元素包含在 _quiz_question 文件中,以便您可以使用 jquery.
获取它
因此它看起来与此类似,但它会根据您的具体实施而有所不同:
# First create the question HTML partial (ensure this has the answer-
# buttons element inside it):
question_html = ApplicationController.render(partial:
'quizzes/_quiz_question', locals: {question: @question})
# Next create the answer HTML partial:
answer_html = ApplicationController.render(partial:
'answers/_answe_buttonr', locals: {answer: @answer})
# Broadcast both to your channel:
ActionCable.server.broadcast('QuizDataChannel', {
question_html: question_html
answer_html: answer_html
})
# Finally, handle this using jquery in your coffeescript:
received: (data) ->
questionHtml = data.question_html
answerHtml = data.answer_html
# This will replace your body with the question html string:
$('body').html(questionHtml)
# Then add your answer buttons:
$('#answer-buttons').html(answerHtml)
我正在尝试制作一个具有 2 种用户类型和此用例的测验应用程序(非单页应用程序):
- 测验主持人用户和参与者用户正在“等待 页”直到测验开始
- 测验主持人用户点击 "quiz start" 按钮开始测验
- 这会立即强制所有参与者用户重定向到带有测验问题的新页面
我按照教程在我的应用程序中设置了 ActionCable,但我想知道的是如何实现第 3 步。我当前的频道 coffeescript 文件如下所示:
# \app\assets\javascripts\channels\quiz_data.coffee:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
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) ->
# Called when there's incoming data on the websocket for this channel
if current_student
# HERE I want to render the page "quiz_question.html.erb"
# ...and then I want to append the answer buttons:
$('answer-buttons').append(data.partial)
send_data: ->
@perform 'send_data'
我很确定答案很简单,但我在谷歌上搜索了很多 Coffeescript 和 ActionCable 教程,几乎所有教程都只呈现用户已经访问的页面的部分内容。我是 Rails 初学者,对 Coffeescript 一无所知,所以任何帮助将不胜感激!
编辑:
这是我的 coffeescript 文件在尝试遵循莱思的回答后的样子:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
connected: ->
# Called when the subscription is ready for use on the server
$('#btn btn-primary btn-lg').on 'click', (e) -> App.quiz_data.send_data()
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
# Called when there's incoming data on the websocket for this channel
if current_student
pageHtml = data.page_html
answerHtml = data.answer_html
# This will replace your body with the page html string:
$('body').html(pageHtml)
# Then add your answer buttons:
#$('#answer-buttons').html(answerHtml)
$('answer-buttons').append(answerHtml)
send_data: ->
@perform 'send_data'
这是我创建的用于渲染我的部分并广播它们的作业:
# app\assets\jobs\quiz_data_broadcast_job.rb:
class QuizDataBroadcastJob < ApplicationJob
queue_as :default
def perform(answers)
ActionCable.server.broadcast('quiz_data', {
page_html: render_page,
answer_html: render_answer_options(answers)
})
end
private
def render_page
ApplicationController.render(
partial: 'pages/student/quiz_question',
locals: {}
)
end
def render_answer_options(answers)
answers.each do |answer|
ApplicationController.render(
#render student/quiz_question page and render as many answer_option partials as needed
partial: 'pages/student/answer_option',
locals: {answer: answer}
)
end
end
end
编辑 2:
这是我的 Javascript 控制台所说的:
Uncaught ReferenceError: App is not defined
at quiz_data.self-21fd077347e9c34e83bab1a2d43a8db5b083fff7ed4eaa02e5314aa78f1dba8b.js:2
at quiz_data.self-21fd077347e9c34e83bab1a2d43a8db5b083fff7ed4eaa02e5314aa78f1dba8b.js:23
这就是我点击它时显示的内容:
1 (function() {
2 App.quiz_data = App.cable.subscriptions.create("QuizDataChannel", {
3 connected: function() {
4 return $('#btn btn-primary btn-lg').on('click', function(e) {
5 return App.quiz_data.send_data();
6 });
7 },
8 disconnected: function() {},
9 received: function(data) {
10 var answerHtml, pageHtml;
11 if (current_student) {
12 pageHtml = data.page_html;
13 answerHtml = data.answer_html;
14 $('body').html(pageHtml);
15 return $('answer-buttons').append(answerHtml);
16 }
17 },
18 send_data: function() {
19 return this.perform('send_data');
20 }
21 });
22
23 }).call(this);
编辑 3: 这是我的 cable.js:
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the rails generate channel command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
WebSockets 并非真正设计用于执行 HTTP 重定向。但是,你仍然可以通过一些jQuery.
给人一种页面发生变化的感觉由于您将在主持人点击开始后同时创建问题和答案,因此您可以将它们都发送到广播中。
我首先建议将 quiz_question.html.erb
文件更改为部分文件:_quiz_question.html.erb
,这样您就可以生成 HTML 并将其附加到您的正文中。接下来,将 answer-buttons
元素包含在 _quiz_question 文件中,以便您可以使用 jquery.
因此它看起来与此类似,但它会根据您的具体实施而有所不同:
# First create the question HTML partial (ensure this has the answer-
# buttons element inside it):
question_html = ApplicationController.render(partial:
'quizzes/_quiz_question', locals: {question: @question})
# Next create the answer HTML partial:
answer_html = ApplicationController.render(partial:
'answers/_answe_buttonr', locals: {answer: @answer})
# Broadcast both to your channel:
ActionCable.server.broadcast('QuizDataChannel', {
question_html: question_html
answer_html: answer_html
})
# Finally, handle this using jquery in your coffeescript:
received: (data) ->
questionHtml = data.question_html
answerHtml = data.answer_html
# This will replace your body with the question html string:
$('body').html(questionHtml)
# Then add your answer buttons:
$('#answer-buttons').html(answerHtml)