Hi there everybody!
The RubyMine staff is continually striving to supply help for brand spanking new applied sciences for Ruby and Rails. One of the vital thrilling latest additions to Rails is undoubtedly Hotwire, so we’ve ready an outline of this suite of frameworks and a tutorial on find out how to use a very powerful Turbo and Stimulus options in your Rails app with RubyMine. This put up covers Turbo; to study extra about Stimulus help, keep tuned for our subsequent weblog put up.
Hotwire and Turbo
What’s Hotwire?
Hotwire simplifies internet growth by sending HTML as an alternative of JSON over the wire (therefore the identify: it stands for “HTML over the wire”). This reduces the quantity of JavaScript written and despatched to the browser and retains template rendering on the server. Hotwire is made up of a number of frameworks: Turbo, Stimulus, and Strada. On this put up, we’ll check out Turbo.
What’s Turbo?
Turbo supplies easy methods of delivering dwell partial updates to the web page with out having to jot down any JavaScript in any respect. It additionally hastens all hyperlink clicks and kind submissions in your app due to Turbo Drive. On this tutorial, we’ll concentrate on splitting the pages into separate contexts that may be up to date in actual time with out reloading the complete web page. For this, we’ll use the opposite two core ideas of Turbo: Turbo Streams and Turbo Frames.
What are Turbo Frames?
Turbo Frames allow you to decompose the web page into unbiased contexts. When you wrap part of your Doc Object Module (DOM) inside a Turbo Body, you’ll be able to replace simply that body with out having to reload the remainder of the web page.
What are Turbo Streams?
Turbo Streams can be utilized to replace elements of the DOM with out reloading your entire web page. A server can ship a Turbo Stream message as an alternative of HTML of your entire web page; the message will include HTML fragments, the goal factor ID, and the motion that will probably be utilized to them (add to the goal, take away from the goal, replace the goal, and many others.: for a full record, please seek the advice of the documentation). Very like Turbo Frames, Turbo Streams allow you to ship real-time partial web page modifications. In some methods, the performance of Turbo Streams is extra sturdy: they allow you to change a number of elements of DOM and never simply the corresponding body, in addition to carry out a wide range of updates, resembling appending or eradicating parts.
The turbo-rails
gem is shipped by default with Rails 7, so you can begin utilizing it in your purposes immediately!
RubyMine, an IDE for Ruby and Rails builders, supplies code perception help for Turbo, resembling code completion and navigation, which we encourage you to make use of as you comply with this tutorial.
Tutorial: find out how to use Turbo in Rails apps with RubyMine
On this tutorial, we’ll present you find out how to use the fundamental constructing blocks of Turbo. We’ll use a pattern Rails software that enables customers to make accounts, create microposts, comply with one another, and skim the microposts in a feed.
Clone the pattern Rails app
Comply with these steps to clone our pattern app and get it operating:
- Take a look at the pattern software at https://github.com/JetBrains/sample_rails_app_7th_ed/tree/hotwire_setup (be sure that to change to the
hotwire_setup
department when you’ve cloned the venture). For additional info on find out how to arrange a Git repository, please see the documentation. - Specify the Ruby interpreter and set up gems.
Easy methods to use Turbo Frames
We are able to at the moment delete posts on this pattern app. Let’s add enhancing, too.
- Open the file
_micropost.html.erb
and add an “Edit” hyperlink subsequent to the “Delete” hyperlink within the timestamp.
<span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> in the past. <% if current_user?(micropost.consumer) %> <%= link_to "delete", micropost, information: { "turbo-method": :delete, turbo_confirm: "You positive?" } %> <%= link_to "edit", edit_micropost_path(micropost) %> <% finish %> </span>
2. Add edit
and replace
strategies to the micropost controller (don’t fear – the routes have already been added to routes.rb
).
def edit @micropost = Micropost.discover(params[:id]) finish def replace @micropost = Micropost.discover(params[:id]) if @micropost.replace(micropost_params) redirect_to root_url else render :edit, standing: :unprocessable_entity finish finish
The edit
button will redirect us to the micropost/[id]/edit
route, which can need to load the view template. Click on on the gutter icon subsequent to def edit
to create a template file edit.html.erb
.
3. Add the next code to your microposts/edit.html.erb
file:
<%= render 'shared/micropost_form'%>
With a view to edit a put up, we merely must render the prevailing partial shared/micropost_form
which we already use for creating microposts. If we click on on Edit now, we’ll be redirected to a brand new web page.
However what we’d love to do right here is to have in-place enhancing: as an alternative of loading a brand new web page, let’s substitute the prevailing micropost physique with a kind, replace the textual content, put it aside – and keep on the identical web page all through.
To do this, we will make the most of Turbo Frames.
With Turbo Frames, we’ll have the ability to load the shape rather than the micropost, replace its content material, after which render the up to date micropost with out having to reload the remainder of the web page.
4. Add a turbo_frame_tag
view helper to _micropost.html.erb
:
<%= turbo_frame_tag micropost do %> <li id="micropost-<%= micropost.id %>"> # maintain the remainder of the file content material because it was </li> <% finish %>
We’ll have the ability to use this body to specify which elements of HTML ought to be changed when the server processes a request. A body ought to have a singular ID which is handed as an argument to turbo_frame_tag
. It may be a string or an emblem, however in our case, we want it to be distinctive to verify we’re working with the appropriate body, so it must depend upon the particular file. Fortunately, Turbo Frames are very handy to make use of: we will simply move the micropost
object, and turbo_frame_tag
will robotically apply the dom_id
helper to it and use that because the body ID.
Since we’d like to exchange our micropost with a kind to edit it, we’ll want edit.html.erb
to include an identical Turbo Body.
5. In microposts/edit.html.erb
, add the turbo_frame_tag
helper:
<%= link_to "Again", root_path %> <%= turbo_frame_tag @micropost do %> <%= render "shared/micropost_form", micropost: @micropost %> <% finish %>
Please observe that the body IDs handed to the turbo_frame_tag
helper ought to match on each pages.
Now, once we click on on the Edit hyperlink, the controller responds to the microposts/[id]/edit
request with a Turbo Body. The content material of this new Turbo Body replaces the outdated content material, and we now see a micropost kind as an alternative of the micropost content material and might edit its textual content. Once we click on on Submit and ship a request to the microposts/[id]/replace
route, the controller as soon as once more sends us a response containing Turbo Frames; Turbo finds the matching body and makes use of its contents to exchange the prevailing ones: on this case, the up to date micropost. You may examine the console to see how Rails processes the requests.
Any hyperlink or kind inside a Turbo Body will probably be intercepted and can attempt to discover a Turbo Body of the identical ID as its father or mother body or its goal (hyperlinks can goal completely different frames; to do this, add a information: { turbo_frame: [frame id] }
as an argument for link_to
).
However what if you wish to replace a number of parts on the web page with a single click on of a hyperlink? Or append or take away parts as an alternative of changing them? That is the place Turbo Streams come in useful.
Easy methods to use Turbo Streams
To exhibit the facility of Turbo Streams, we’ll add an unfollow hyperlink to every micropost and replace the consumer.following
rely in actual time when the present consumer unfollows somebody from their feed. We’ll additionally take away all of the posts by the unfollowed consumer.
- Open
_micropost.html.erb
and add an unfollow hyperlink to every micropost not made by the present consumer:
<% if current_user?(micropost.consumer) %> <%= link_to "delete", micropost, information: { "turbo-method": :delete, turbo_confirm: "You positive?" } %> <%= link_to "edit", edit_micropost_path(micropost) %> <% elsif !current_user.nil? && current_user.following?(micropost.consumer) %> <%= link_to "unfollow", relationship_path(current_user.active_relationships.find_by(adopted: micropost.consumer), user_to_update: current_user), information: {turbo_method: :delete, turbo_confirm: 'Are you positive?'} %> <% finish %>
The place does the trail come from? The present app already implements performance that enables customers to comply with and unfollow one another; furthermore, this performance even makes use of Turbo Streams. To attain a greater understanding of Turbo, we’ll take that implementation up a step and permit customers to unfollow different customers not simply from the consumer web page, but additionally from their feed.
The Consumer
mannequin maintains the record of all of the customers a consumer follows (active_relationships)
and all of the customers that comply with that consumer (passive_relationship)
. To unfollow a consumer, we have to destroy this active_relationships
affiliation between the 2 customers; due to this fact, we have to acquire the proper path and set the hyperlink to make use of the DELETE
methodology (which, due to Turbo, we will do out of the field).
Let’s see what occurs once we click on on unfollow:
Not solely are the posts of the consumer we simply unfollowed nonetheless current on our feed, however the counter replace, though dwell, can be mistaken.
To repair this, let’s first check out the controller that manages relationships.
2. Open the file relationships_controller.rb
.
The present code is as follows:
# relationships_controller.rb @consumer = Relationship.discover(params[:id]).adopted current_user.unfollow(@consumer) respond_to do |format| format.html { redirect_to @consumer, standing: :see_other } format.turbo_stream finish
It’s designed for unfollowing customers from the Customers web page of the pattern app: as the present consumer, you unfollow one other consumer after which see their follower rely lower. Nonetheless, in our case, we’d wish to unfollow a consumer from our feed web page after which see the present consumer’s following
rely lower. To do this, we simply must know which consumer (present or unfollowed consumer) we’re about to replace the counters for.
3. Open the file routes.rb
and add a route with an additional parameter that may allow us to move the ID of the consumer for whom we need to replace the counter:
sources :relationships, solely: [:create, :destroy] delete '/relationships/:id/:user_to_update', to: 'relationships#destroy_with_counter_update'
Now that we’re passing a parameter and utilizing a customized route, we’ll must replace the trail we’ll be utilizing for the unfollow hyperlink:
<%= link_to "unfollow", "/relationships/#{current_user.active_relationships.find_by(adopted: micropost.consumer).id}/#{current_user.id}/", information: {turbo_method: :delete, turbo_confirm: 'Are you positive?'} %>
We are able to now retrieve the consumer from the parameters when updating relationships_controller.rb
.
4. Add the next code to relationships_controller.rb
:
# relationships_controller.rb def destroy_with_counter_update relationship = Relationship.discover(params[:id]) @unfollowed_user = relationship.adopted current_user.unfollow(@unfollowed_user) @consumer = Consumer.discover(params[:user_to_update]) @microposts = Micropost.the place(:user_id => @unfollowed_user) respond_to do |format| format.html { redirect_to @unfollowed_user, standing: :see_other } format.turbo_stream finish finish
This methodology handles unfollowing customers from the feed and instantly updating the following
counters.
By this level, you could be questioning what format.turbo_stream
is and what this methodology does.
Turbo interjects its particular turbo_stream
format into the Settle for header of the request (that is performed by default if the shape is submitted with a POST, PUT, PATCH,
or DELETE
methodology, however you’ll be able to manually add this format to your GET
requests as properly; see the documentation). The controller can then ship a response on this format or ignore it and fall again on HTML. That is precisely what’s occurring within the code for RelationshipsController#destroy
: the controller can ship responses in each HTML and Turbo Stream codecs.
format.turbo_stream
can render the response inline within the controller:
format.turbo_stream { render turbo_stream: turbo_stream.replace "following", @consumer.following.rely }
When you’d wish to replace a number of targets, it’s preferable to make use of the corresponding view template with the extension .turbo_stream.erb
.
5. Create a file named relationships/destroy_with_counter_update.turbo_stream.erb
and paste the next code there:
<%= turbo_stream.replace "followers" do %> <%= @consumer.followers.rely %> <% finish %> <%= turbo_stream.replace "following" do %> <%= @consumer.following.rely %> <% finish %> <%= microposts = Micropost.the place(:user_id => @unfollowed_user) %> <% microposts.every do |put up| %> <%= turbo_stream.take away put up %> <% finish %>
Let’s take a better have a look at the code that updates the Following rely:
<%= turbo_stream.replace "following" do %> <%= @consumer.following.rely %> <% finish %>
What do these traces do?
The controller must ship some HTML in response to a request. Turbo lets us ship fragments of HTML with out sending your entire web page. On this case, solely these fragments are up to date, with no full web page reload. These traces specify the Turbo Streams message that the controller will ship.
The HTML for these messages takes on the next kind:
<turbo-stream motion="[action name]" goal="[target element or Turbo Frame ID]"> <template> The contents of this tag will probably be utilized to the goal factor as per the desired motion, e.g. deleted, appended, changed, and many others. </template> </turbo-stream>
Turbo Streams let the customers execute eight actions in complete: append, prepend, substitute, replace, take away, earlier than, after,
and morph
. For extra info, please seek the advice of the documentation.
Let’s check out the code we added to destroy.turbo_stream.erb
once more:
<%= turbo_stream.replace "following" do %> <%= @consumer.following.rely %> <% finish %>
We need to carry out the replace
motion on the following goal (you could discover the DOM factor with this ID within the shared/_stats.html.erb
partial), and we wish the goal to be up to date with the @consumer.following.rely
HTML.
Now let’s check out the code that removes all of the posts by the unfollowed consumer from the feed:
<%= microposts = Micropost.the place(:user_id => @unfollowed_user) %> <% microposts.every do |put up| %> <%= turbo_stream.take away put up %> <% finish %>
As you’ll be able to see, now we will unfollow customers proper from the feed. The next rely will replace appropriately and in actual time, and the posts will disappear from the feed – all with no full-page reload!
Easy methods to use broadcasting
One other approach to obtain that is broadcasting.
Turbo Streams might be broadcast from fashions upon updates to the file. There are a number of primary actions that may be broadcast: take away, substitute, append, prepend, earlier than, after,
and replace
. For extra info, please seek the advice of the documentation.
In our case, we’d wish to broadcast updates when one consumer unfollows one other – that’s, when an occasion of Relationship
is destroyed.
Firstly, we’ll must destroy the file in RelationshipsController#destroy
.
- Open
relationships_controller.rb
, navigate to thedestroy_with_counter_update
methodology, and add the next line:
#relationships_controller.rb relationship.destroy!
Since broadcasting is finished in a mannequin callback, we’ll must explicitly destroy the file first.
2. Open the file relationship.rb and add the next line:
# relationship.rb after_destroy { broadcast_update_to "following_stream_#{follower.id}", goal: "following", html: "#{follower.following.rely}" }
after_destroy
is the callback that executes after a Relationship file has been destroyed. broadcast_update_to
is the strategy that enables us to broadcast the replace
motion to Turbo Streams. You may broadcast different actions, too:
Let’s check out the arguments to broadcast_update_later_to
. First, now we have to specify the stream we’ll be broadcasting to. In our case, we’d just like the stream identify to depend upon the consumer ID to make sure we’re broadcasting the appropriate info to the appropriate customers; in any other case, we would, for instance, lower the counter for all of the customers and never only for the present one. Due to this fact, let’s identify our stream following_stream_#{follower.id}
. We additionally need to specify the goal: it’s nonetheless the identical DOM factor with the ID following
. Lastly, we have to present the HTML fragment to be rendered. Steadily, it’s a partial view, wherein case the arguments would appear like this:
after_destroy { broadcast_update_later_to "[stream name]", goal: "[target ID]", partial: "[partial name]", locals: { ... } }
In our case, it is sufficient to move the HTML.
With a view to obtain broadcasts, now we have to subscribe to streams of the corresponding identify within the view information.
3. Open the file shared/_stats.html.erb
. After the primary line, add the next:
<% @consumer ||= current_user %> <%= turbo_stream_from "following_stream_#{@consumer.id}" %>
And don’t overlook to take away the code we added to destroy.turbo_stream.erb
that updates the next counter, since we’re now updating it by way of broadcasting as an alternative.
Now, if we load the web page and unfollow a consumer, we’ll see the counter being up to date in actual time.
When ought to we use broadcasting as an alternative of sending Turbo Streams from the controller?
It’s best to use broadcasting if:
- The replace ought to be seen not simply to the present consumer, however to everybody viewing the identical web page
- The replace is asynchronous
If neither of these is required, you need to use Turbo Streams from the controller.
Conclusion
On this tutorial, we’ve explored the Turbo framework and its core ideas: Turbo Streams and Turbo Frames. We’ve discovered to make use of Turbo Frames and Turbo Streams in our Rails purposes to ship real-time partial updates to the pages.
Turbo Frames allow us to ship partial dwell updates to sure DOM parts, whereas Turbo Streams allow us to goal a number of parts and carry out a wide range of actions. Nonetheless, generally we would need extra interactivity than Turbo Streams enable, and thus, we should flip to JavaScript.
For extra particulars on find out how to use Turbo Frames and Turbo Streams with RubyMine, take a look at our video tutorial:
Fortunately, Hotwire supplies one other element to do exactly that: Stimulus.
For extra details about Stimulus, please keep tuned for our subsequent weblog put up.
Make the most of Hotwire help in your favourite JetBrains IDE for Ruby and Rails. Obtain the newest RubyMine model from our web site or by way of the freeToolbox App.
To study in regards to the latest options as they arrive out, please comply with RubyMine on X.
We invite you to share your ideas within the feedback under and to counsel and vote for brand spanking new options within the situation tracker.
Comfortable growing!
The RubyMine staff
Subscribe to RubyMine Weblog updates