On Rails 7, I have created a channel called "SessionChannel". When my page loads, the consumer is being properly created as I can see the log show in the browser.
This is my app/javascript/channels/session_channel.js:
import consumer from "channels/consumer"
var channel = consumer.subscriptions.create("SessionChannel", {
connected() {
console.log("Connected to session channel");
// Called when the subscription is ready for use on the server
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
console.log("got data: " + data);
// Called when there's incoming data on the websocket for this channel
}
});
setTimeout(() => {
channel.send({ some_data_to_rails: "goes_here" })
console.log("sent");
}, 3000);
This works fine and the channel.send
properly sends data back up the channel. But it seems I can only access the channel
variable inside this file. If I try to access it in the browser or the application.js for example, it's undefined.
The question: Is it possible to export this variable (consumer/channel instance) to the app-wide javascript scope? So that I can channel.send
from any view? Or at least be able to do it in the browser?
You can access it from a browser console and from inline scripts if you add it to window:
const channel = consumer.subscriptions.create("SessionChannel", {
// ...
});
window.channel = channel;
However, that's not necessary:
// app/javascript/channels/session_channel.rb
import consumer from "channels/consumer";
// NOTE: make sure to export it
export default consumer.subscriptions.create("SessionChannel", {
received(data) {
console.log("SessionChannel", data);
},
});
# app/channels/session_channel.rb
class SessionChannel < ApplicationCable::Channel
def subscribed
stream_from "session_channel"
end
def receive(data)
ActionCable.server.broadcast "session_channel", data
end
end
You can create a stimulus controller to make it "accessible from the veiw":
// app/javascript/controllers/hello_controller.rb
import { Controller } from "@hotwired/stimulus";
// NOTE: now you have something to import
// you are working with modules, so `sessionChannel`
// will only be available in this file, if you need it
// somewhere else then you have to import it there as well.
import sessionChannel from "channels/session_channel";
export default class extends Controller {
static targets = ["message"];
send(event) {
event.preventDefault();
sessionChannel.send({ message: this.messageTarget.value });
this.messageTarget.value = "";
}
}
Use it in any view:
<!-- app/views/home/index.html.erb -->
<div data-controller="hello">
<%= text_area_tag :message, "", data: { hello_target: "message" } %>
<%= button_tag "say: hello", data: { action: "hello#send" } %>
</div>
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import