I'm trying to make a quiz application (non single page app) with 2 user types and this use case:
- the quiz-hoster-user and the participant-users are on a "waiting page" until the quiz starts
- the quiz-hoster-user clicks on a "quiz start"-button to start the quiz
- this immediately forces the all the participant-users to be redirected to a new page with the quiz question
I followed a tutorial and set up ActionCable in my app, but what I would like to know is how to achieve step 3. My current coffeescript file for my channel looks like this:
# \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'
I'm pretty sure the answer is very simple, but I've googled a lot of Coffeescript and ActionCable tutorials, and almost all of them only render partials to a page that the user is already on. I'm a Rails beginner and know nothing of Coffeescript, so any help would be appreciated!
EDIT:
This is what my coffeescript file looks like after trying to follow Laiths answer:
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'
And this is the Job I created to render my partials ans broadcast them:
# 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
EDIT 2:
This is what my Javascript Console says:
Uncaught ReferenceError: App is not defined
at quiz_data.self-21fd077347e9c34e83bab1a2d43a8db5b083fff7ed4eaa02e5314aa78f1dba8b.js:2
at quiz_data.self-21fd077347e9c34e83bab1a2d43a8db5b083fff7ed4eaa02e5314aa78f1dba8b.js:23
And this is what is shows me when I click on it:
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);
EDIT 3: This is my 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);