Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 6, 2021 02:00 am GMT

A simple guide to Action Cable

Action cable is the Rails way of implementing WebSockets - with some Rails magic.

Why use it

Usually your client to connects with your server by making requests:
953F8214-7173-4755-B16E-AB6FC9983223

With ActionCable, you create an open connection between your client and your server, allowing a communication flow:
65223B82-BB95-4D36-8FC0-7EE6B1227ED4

Example:

You have a simple blog - posts and comments - and multiple users reading that post. If one user adds a comment, the other one will never know:
Kapture 2021-05-05 at 22.53.50

But with the open connection from ActionCable, he will receive updates from that posts (the important comments from other people)
Kapture 2021-05-05 at 22.44.44

How to do it

First of all, generate a channel for your Posts. This class will be able to broadcast updates to all clients listening:

rails generate channel posts

Which will create some files for you:

      create    test/channels/posts_channel_test.rb      create  app/channels/posts_channel.rb   identical  app/javascript/channels/index.js   identical  app/javascript/channels/consumer.js      create  app/javascript/channels/posts_channel.js

Sending messages

We will work with our newly generated posts_channel.rb

We want to specify from which channel to stream, so we can pass the an idparams and ask rails to make a stream for that post:

class PostsChannel < ApplicationCable::Channel  def subscribed    post = Post.find(params[:id])    stream_for post  endend

And now, from anywhere in our app, we can call PostsChannel and ask it to broadcast something to anyone listening to that post:

PostsChannel.broadcast_to(@post, @comment.body)

We will add this to our create action, to broadcast the comment to the post channel every time a comment is created:

# app/controllers/comments_controller.rbdef create  @comment = @post.comments.new(comment_params)    if @comment.save      PostsChannel.broadcast_to(@post, @comment.body)      redirect_to @post, notice: "Comment was successfully created."    else      render :new    endend

And that does nothing so far, since no one is listening to this broadcast. Moving forward!

Receiving messages

Opinionated setup:

I do not like to create a separate file for every consumer, I prefer to do the connection in script tags in the view. It feels more like a separate front end, where only the views that need a connection create one. To do so, change your app/javascript/channels/consumer.js to expose the action cable:

import { createConsumer } from "@rails/actioncable"(function() {  window.App || (window.App = {});  window.App.cable = ActionCable.createConsumer();}).call(this);export default createConsumer()

Note: Exposing the cable was the default according to official docs until Rails 6, where Webpacker was introduced

By default, the generator we used before creates a file in app/javascript/channels/posts_channel.js
This might be bad for some reasons:

  • It it always required, so it always run
  • We might not want to have everyone on our app to try and open a connection, just the ones listening to our post show page

So you can go ahead and delete the created posts_channel.js

Now, we can make the listener to our broadcast in the view:

<!-- app/views/posts/show.html.erb --><script>  App.cable.subscriptions.create({ channel: "PostsChannel", id: "<%= @post.id %>" }, {    connected() {      console.log("Connected to the channel:", this);    },    disconnected() {      console.log("Disconnected");    },    received(data) {      console.log("Received some data:", data);    }  });</script>

And now, upon opening our blog post page, we can see the connected message on our Console, and some Rails main that enabled this connection on our terminal:
82E5203A-8D8C-494E-8810-0CEACBF4AEAB

The posts:Z2lkOi8vYWN0aW9uY2FibGUtYXBwL1Bvc3QvMg is the name of the channel created by rails when we told it to stream_for post in our posts_channel file.

And youre done!
~Almost~

While the above script received data, it doesnt show it on the page. We can update it to add the comment to our list upon receiving it:

  App.cable.subscriptions.create({ channel: "PostsChannel", id: "<%= @post.id %>" }, {    received(comment) {      el = document.createElement('li');      el.innerHTML = comment;      document.querySelector('ul').appendChild(el)    }  });

(All functions are optional, I removed the disconnected and connected from here)

And there you go, your app now talks to any browser listening to it via Action Cable:
Kapture 2021-05-05 at 22.44.44

References

Rails Guide:
https://guides.rubyonrails.org/action_cable_overview.html
Heroku Guide:
https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable
Cable.yml Config:
https://github.com/rails/rails/issues/28118
Cable for specific pages:
https://stackoverflow.com/questions/39597665/rails-actioncable-for-specific-pages
https://stackoverflow.com/questions/36438323/page-specific-actioncable
Good JS subscription examples:
https://stackoverflow.com/questions/39597665/rails-actioncable-for-specific-pages
https://samuelmullen.com/articles/introduction-to-actioncable-in-rails-5/
https://stackoverflow.com/questions/36266746/actioncable-not-receiving-data
Usage with ActiveJob
https://www.pluralsight.com/guides/creating-a-chat-using-rails-action-cable
Cable on ReactNative
https://stackoverflow.com/questions/43510021/action-cable-not-receiving-response-on-client
AnyCable
Action Cable vs AnyCable: fight! | Nebulab


Original Link: https://dev.to/lucaskuhn/a-simple-guide-to-action-cable-2dk2

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To