Hotwire Demo — Turbo Frame
Decomposes Pages Into Independent Contexts HotwireSeries_Episode#1
In this post :
. Learn about Turbo Frame.
We are now prepared to delve into Hotwire!
For your convenience, we have uploaded this code to my GitHub repository. Feel free to start without needing to type anything on your keyboard.
👉️ GitHub repo
First:
TURBO FRAME!
Turbo Frames decomposes pages into independent contexts, which can be lazy-loaded and scoped for interaction.
Turbo frames can be lazy-loaded and scoped for interaction.
This sentence is referring to two concepts related to Turbo Frames in the context of Hotwire:
- Lazy Loading: “Turbo frames can be lazy loaded.” This means that Turbo Frames can be loaded into the page only when they become visible in the user’s viewport. In other words, the content within the Turbo Frame won’t be loaded until it’s needed or until the user scrolls to that part of the page. This can help improve the initial loading performance of a web page by deferring the loading of non-essential content until necessary.
- Scoped Interaction: “Scoped for interaction” suggests that the Turbo Frames provide an isolated scope for user interaction. When you interact with elements or perform actions within a Turbo Frame, those interactions are contained within that specific frame. This isolation can help manage the state and behavior of different sections of the page separately, making the application more modular and easier to manage.
In essence, Turbo Frames in Hotwire provide the capability to load content on-demand (lazy loading) while keeping interactions within a specific scope. This combination of features contributes to a more efficient and interactive user experience on web pages.
0#Step — Let’s apply a noticeable blue border to turbo-frame
in order to readily identify the frame that will undergo dynamic changes.
GoTo:
chat/app/assets/stylesheets/application.css
turbo-frame {
display: block;
border: 1px solid blue;
}
In the absence of a Turbo Frame, when we click Edit
, we are directed to a new page containing the form, which takes us away from the main page for editing the room title. By utilizing Turbo Frame, we consolidate all these actions within a single page, achieving a single-page application (SPA) experience.
1#Step — Let us employ Turbo Frames to enable dynamic updates within the room
frame, which contains the editing form.
Go To:
chat/app/views/rooms/show.html.erb
<p id="notice" style="color: green"><%= notice %></p>
<turbo-frame id="room">
<%= render @room %>
<p>
<%= link_to "Edit", edit_room_path(@room) %>
<%= link_to "Back", rooms_path %>
</p>
</turbo-frame>
<div id="messages">
<%= render @room.messages %>
</div>
<%= link_to "New Message", new_room_message_path(@room) %>
The central element of this code snippet is the following segment:
<turbo-frame id="room">
<!-- ... -->
</turbo-frame>
This section uses the Turbo Frames feature, which is part of the Hotwire framework. It wraps the content inside a frame with the id
attribute set to room
. The content inside will be replaced dynamically using Turbo Streams when changes occur.
This specific section leverages the Turbo Frames feature, an integral aspect of the Hotwire framework. It encapsulates the enclosed content within a frame, distinctly identified by the room
id attribute. As changes transpire, Turbo Streams will seamlessly and dynamically replace the content within this frame.
2#Step — Go To:
chat/app/views/rooms/edit.html.erb
<h1>Editing room</h1>
<turbo-frame id="room">
<%= render "form", room: @room %>
<br>
</turbo-frame>
<div>
<%= link_to "Show this room", @room %> |
<%= link_to "Back to rooms", rooms_path %>
</div>
This is the target page. Let’s discuss the rules applied to Turbo Frames:
Rule #1: When clicking a link within a Turbo Frame,
Turbo expects a frame with
the identical ID on the target page.
The central element of this code snippet is <turbo-frame id="room"> ... </turbo-frame>
: This part employs the Turbo Frames feature. It defines a frame with the ID attribute set to room
. The frame contains the rendered content from the _form
partial, with the room
variable being passed as a parameter. This setup will update specific parts of the page dynamically without reloading the entire page (see show.html.erb
).
For completeness:
Rule #2: If there is no Turbo Frame with
the same ID on the target page,
the frame desappears, raising an
ERROR response (no matching element)
on the console.
And finally:
Rule #3: A link can target another frame
than the one it is directly nested in
- use data-turbo-frame attribute
More info about adding the data-turbo-frame
attribute on non-frame elements to control this, click here.
3#Step — If you conduct a test, you’ll notice that the Back
link is currently malfunctioning. To address this, follow these steps:
Complete the following line:
chat/app/views/rooms/show.html.erb
<%= link_to "Back", rooms_path, "data-turbo-frame": "_top" %>
By incorporating the data-turbo-frame
attribute as shown above. This attribute is set to _top
, enabling a breakout from the frame in a manner reminiscent of traditional HTML frames.
As a result of this adjustment, the Back
link will now function as intended, allowing seamless navigation, while the frame continues to encapsulate the Edit Display Loop.
4#Step —Let’s incorporate the New Message
link within an inline Turbo Frame tag that loads lazily. This frame will be fetched immediately after the initial page load:
Navigate to: chat/app/views/rooms/show.html.erb
Replace the line:
<%= link_to "New Message", new_room_message_path(@room) %>
By this one:
<turbo-frame id="new_message" src="<%= @room.id %>/messages/new" target="_top">
</turbo-frame>
5#Step — Now the target page:
chat/app/views/messages/new.html.erb
<h1>New Message</h1>
<turbo-frame id="new_message" target="_top">
<%= form_with(model: [@message.room, @message],
data:{ controller:"reset_form", action: "turbo:submit-end->reset_form"}) do |form| %>
<div class="field">
<%= form.text_field :content %>
<%= form.submit "Send"%>
</div>
<% end %>
</turbo-frame>
<%= link_to 'Back', @message.room %>
Now Let’s turn to TURBO STREAMS!
However, this topic will be addressed in the upcoming post!
See you soon!
👉️GitHub — Hotwire_v2
Related Posts:
0#Episode — HotwireSeries — Intro — Hotwire Demo — What is the purpose of Hotwire in Rails 7?
1#Episode — HotwireSeries — Turbo Frame — Decomposes Pages Into Independent Contexts (this one)
2#Episode — HotwireSeries — Turbo Stream — Pushing HTML Updates To The Client Using WebSockets
3#Episode — HotwireSeries — Stimulus — A lightweight JavaScript Framework
GitHub cmds:
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
modified: app/assets/stylesheets/application.css
modified: app/views/messages/new.html.erb
modified: app/views/rooms/edit.html.erb
modified: app/views/rooms/show.html.erb
no changes added to commit (use "git add" and/or "git commit -a")
$ git branch --list
$ git add -A
$ git commit -m ":sparkles: feat: Add Turbo Frame"
[master 2214db2] :sparkles: feat: Add Turbo Frame
5 files changed, 57 insertions(+), 31 deletions(-)
$ git push
Enumerating objects: 25, done.
Counting objects: 100% (25/25), done.
Delta compression using up to 12 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (13/13), 1.79 KiB | 1.79 MiB/s, done.
Total 13 (delta 8), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (8/8), completed with 8 local objects.
To github.com:giljr/hotwire_rails_tutorial.git
11d4a66..2214db2 master -> master
$ git tag -a Hotwire_v2 -m "Turbo Tech - How it Works! Go to https://hotwired.dev/" -m "0- Work with Turbo Frame." -m "Thank you for downloading this project 😘️👌️👋️😍️"
$ git push origin Hotwire_v2
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 274 bytes | 274.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:giljr/hotwire_rails_tutorial.git
* [new tag] Hotwire_v2 -> Hotwire_v2
Fix_1: Step#4 — thanks to Sia Davarnia on Jan 2024