Rails 如何使用 ActionCable 在 "received:" 上呈现全新页面

Rails How to use ActionCable to render whole new page on "received:"

我正在尝试制作一个具有 2 种用户类型和此用例的测验应用程序(非单页应用程序):

  1. 测验主持人用户和参与者用户正在“等待 页”直到测验开始
  2. 测验主持人用户点击 "quiz start" 按钮开始测验
  3. 这会立即强制所有参与者用户重定向到带有测验问题的新页面

我按照教程在我的应用程序中设置了 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)