Sitemap

Turbo Broadcast

4 min readApr 2, 2023

--

Press enter or click to view image in full size

What is Turbo Broadcasting?
Turbo Broadcast is a feature of the Ruby on Rails web framework that allows for the real-time broadcasting of messages to multiple clients. It enables developers to create reactive, dynamic web applications that broadcast updates to multiple users in real-time.

Benefits:

  • It makes application reactive and dynamic
  • Real-time update for all clients

Working:
lets Imagine i have a social application in which user can create post and then comment on that post’s

here is how posts/index.html.erb look

<%= turbo_stream_from :posts_list %>
<div id='all_posts'>
<%= render @posts %>
</div>

Break down the the above code:
when we add <%= turbo_stream_from :posts_list %> this it will crate turbo stream channel with name “posts_list” and after refresh the posts/index if you inspect the page you will see the below line.

<turbo-cable-stream-source channel="Turbo::StreamsChannel" signed-stream-name="InBvc3RzX2xpc3Qi - d9b974543bdecbb95be7b391f3b71655f68346bc92b4c97fc217a4c6ad35f570"></turbo-cable-stream-source>

<div id=’all_posts’> it’s the element with id we will use in broadcast as target to tell where we need to broadcast the new, updated or deleted data.
and <%= render @posts %> its just the collection rendering of post’s

Here is how posts/_post.html.erb post partial will look like

<div class="posts-container" id=<%= dom_id(post) %>>
<span>
post.body
</span>
<span id="<%= dom_id(post) %>_comments_count">
<%= post.comments.count if post.comments.present? %>
</span>
</div>

dom_id(post) will assign uniq id to every post in the form ( post_1) like post_post.id and we will use as a target for post deletion and updation because these operation are related to single post

Broadcasting for create action:
Here is PostsController crate action

def create
@post = current_user.posts.build(post_params)
if @post.save
head 200
else
render :new, flash.now[:notice] = "Message n sent"
end
end

We will add below line in Post model for broadcast the create action
after_create_commit

after_create_commit { broadcast_prepend_later_to :posts_list, target: 'all_posts', partial: 'posts/post', locals: { post: self } }

here is break down of above broadcast line, explanation in single line below

{ broadcast_prepend_later_to :stream_name, target: 'wrapper_id_in_which_all_posts_will_rendered', partial: 'partial/in/which/self/will/place/then/this/partial/will/place/in/target/element', locals: { post: self } }

when post will save then after_create_commit call back will execute and it check which the stream_name in our case is “posts_list” in the posts/index.html.erb after that it will find the target element (in our case is “all_posts”) in the page where “posts_list” stream exist and then it will add the “self” in the “post” variable and take it to the “posts/post” partial and create new post and then add the newly crated post at the start of all posts in the posts/index.html.erb because we use “broadcast_prepend_later_to”
the “prepend” key word if we want to add it at the end of all posts then replace “prepend” with “append” like “broadcast_append_later_to”

Get Umeriqbal’s stories in your inbox

Join Medium for free to get updates from this writer.

Similarly for update action in controller

 def update
if @post.update(post_params)
head 200
else
render :edit
end
end

and in post model broadcast for update action will look like the below one

after_update_commit {broadcast_replace_later_to :posts_list, target: self, partial: 'posts/post', locals: { post: self } }

in this case we assign self to target insted of “all_posts” because we want to update the particular post not all the posts and here it will behave like “post_1” as the dom_id(post) create “post_1” in the posts/_post.html.erb post partial above if it not work then you can make the target by youself like

after_update_commit {broadcast_replace_later_to :posts_list, target: "post_#{self.id}", partial: 'posts/post', locals: { post: self } }

and For Destroy action in controller:

def destroy
head 200 if @post.destroy
end

and in post model broadcast for update action will look like the below one

after_destroy_commit {broadcast_remove_to :posts_list, target: self }

its little bit different then the create and update like we not have partial and variable because we are not creating or updating the partial but we are just removing the target element in the “posts_list” turbo stream.

— — — — — — — — — —— — broadcasting through controller — — — — — — — — —

def create
@post = current_user.posts.build(post_params)
if @post.save
Turbo::StreamsChannel.broadcast_prepend_later_to(:posts_list, target: 'all_posts', partial: 'posts/post', locals: { post: @post })
head 200
else
render :new, flash.now[:notice] = "Message not sent"
end
end

def edit
end

def update

if @post.update(post_params)
Turbo::StreamsChannel.broadcast_replace_later_to(:posts_list, target: @post, partial: 'posts/post', locals: { post: @post })
head 200
else
render :edit
end
end

def destroy
if @post.destroy
Turbo::StreamsChannel.broadcast_remove_to(:posts_list, target: @post)
head 200
end
end

Now here is how you can update the particular target using broadcasting:-

like in comment controller we have a method count_update and we will call it from create and destroy like below

def destroy
if @comment.destroy
update_posts_count
Turbo::StreamsChannel.broadcast_remove_to(:comments_list, target: "@comment.id">comment_#{@comment.id}")
head 200
end
end

def update_posts_count
Turbo::StreamsChannel.broadcast_update_to :posts_list, target: "@post.id">post_#{@post.id}_comments_count", html: @post.comments.count
end
target: "@post.id">post_#{@post.id}_comments_count"

this is the id we have in our posts/_post.html.erb partal and now we are updating
comments count in post partial form comments controller using broadcast at runtime
comment count will automatically update every time when uer create or delete comment

here how we render view component using broad casting method will be same both if you broadcast from controller or form model:-

Turbo::StreamsChannel.broadcast_prepend_later_to(:comments_list, target: 'all_comments',
html: ApplicationController.render(Comment::CommentComponent.new(comment: @comment, current_user: current_user), layout: false))

--

--

Umeriqbal
Umeriqbal

Written by Umeriqbal

I am a full stack software engineer specializing in Ruby on Rails, I have extensive experience in developing and deploying web applications.