# Ruby on Rails 7: Hotwire va Turbo cho Ung Dung Phan Hoi > Huong dan day du ve Hotwire va Turbo trong Rails 7. Xay dung ung dung phan hoi khong can JavaScript voi Turbo Drive, Frames va Streams. - Published: 2026-01-11 - Updated: 2026-04-07 - Author: SharpSkill - Tags: ruby on rails, hotwire, turbo, turbo frames, turbo streams - Reading time: 12 min --- Rails 7 da cach mang hoa viec phat trien web bang cach tich hop Hotwire mac dinh. Stack nay cho phep xay dung cac ung dung co tinh phan hoi cao ma khong can viet bat ky dong JavaScript tuy chinh nao. Turbo Drive, Turbo Frames va Turbo Streams thay the cac cach tiep can SPA truyen thong bang triet ly "HTML qua day". > **Tai sao Hotwire?** > > Hotwire giam manh do phuc tap cua frontend. Khong can React hay Vue cho giao dien dong: server gui HTML san sang su dung va Turbo tu dong xu ly cac cap nhat DOM. ## Hieu ve Kien Truc Hotwire Hotwire bao gom ba cong nghe bo sung hoat dong cung nhau de mang lai trai nghiem nguoi dung muot ma khong co do phuc tap dien hinh cua cac framework JavaScript. **Turbo Drive** tang toc dieu huong bang cach chan cac click lien ket va gui bieu mau. Thay vi tai lai toan bo trang, chi noi dung `` duoc thay the, giu nguyen ngu canh JavaScript va CSS. **Turbo Frames** chia cac trang thanh cac phan doc lap. Moi frame co the duoc cap nhat rieng biet, cho phep tuong tac co muc tieu ma khong anh huong den phan con lai cua trang. **Turbo Streams** cho phep cap nhat thoi gian thuc qua WebSocket hoac dap ung cac yeu cau HTTP. Tam hanh dong CRUD co san de thao tac DOM khai bao. ```ruby # Gemfile # Installing Turbo Rails (included by default in Rails 7+) gem 'turbo-rails' ``` Doi voi cac du an hien co, viec cai dat chi can mot lenh duy nhat. ```bash # installation.sh # Installing Turbo in an existing Rails project rails turbo:install # Verify the JavaScript import is present cat app/javascript/application.js # Should contain: import "@hotwired/turbo-rails" ``` ## Cau Hinh Ban Dau Turbo Drive Turbo Drive duoc bat mac dinh trong Rails 7. Tat ca dieu huong tu dong tro thanh "turbo" ma khong can cau hinh them. Hanh vi co the tuy chinh thong qua cac thuoc tinh data. ```erb <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %>
<%= yield %> ``` Turbo Drive co the duoc tat tren cac lien ket hoac bieu mau cu the khi can thiet. ```erb <%= link_to "Dashboard", dashboard_path %> <%= link_to "Download PDF", export_path, data: { turbo: false } %> <%= form_with model: @document, data: { turbo: false } do |f| %> <%= f.file_field :attachment %> <%= f.submit "Upload" %> <% end %> <%= link_to "Delete", item_path(@item), data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %> ``` > **Cache Turbo Drive** > > Turbo Drive luu cache cac trang da truy cap. Thuoc tinh `data-turbo-track="reload"` tren tai nguyen buoc tai lai day du neu cac file CSS/JS thay doi. ## Turbo Frames cho Cap Nhat Co Muc Tieu Turbo Frames dinh nghia cac vung trang duoc cap nhat doc lap. Moi frame co mot dinh danh duy nhat va chi phan hoi cac response chua frame trung khop. ```erb

Messages

<%= render @messages %>
<%= link_to "Load more", messages_path(page: @next_page) %>
<%= render "form", message: Message.new %> ``` Phan hoi tu server phai chua frame voi cung dinh danh de cap nhat hoat dong. ```erb

<%= message.content %>

<%= link_to "Edit", edit_message_path(message) %> <%= button_to "Delete", message_path(message), method: :delete %>
``` ### Lazy Loading voi Turbo Frames Cac frame co the tai noi dung bat dong bo su dung thuoc tinh `src`. Noi dung ban dau duoc hien thi trong khi tai. ```erb

Dashboard

Loading statistics...

Loading notifications...

<%= render "activity/placeholder" %> ``` Controller phan hoi voi view chi chua frame duoc yeu cau. ```ruby # app/controllers/dashboard_controller.rb class DashboardController < ApplicationController def stats @user_count = User.count @message_count = Message.count @active_today = User.where("last_seen_at > ?", 24.hours.ago).count # Partial rendering for the frame render partial: "dashboard/stats" end end ``` ```erb

<%= @user_count %>

Users

<%= @message_count %>

Messages

<%= @active_today %>

Active today

``` ## Turbo Streams cho Cap Nhat Thoi Gian Thuc Turbo Streams cung cap tam hanh dong de thao tac DOM: `append`, `prepend`, `replace`, `update`, `remove`, `before`, `after` va `morph`. Cac hanh dong nay co the duoc kich hoat qua phan hoi HTTP hoac WebSocket. ```ruby # app/controllers/messages_controller.rb class MessagesController < ApplicationController def create @message = current_user.messages.build(message_params) respond_to do |format| if @message.save # Turbo Stream response to add the message format.turbo_stream do render turbo_stream: [ turbo_stream.prepend("messages", @message), turbo_stream.update("message_count", partial: "messages/count"), turbo_stream.replace("new_message", partial: "messages/form", locals: { message: Message.new }) ] end format.html { redirect_to messages_path, notice: "Message created" } else format.turbo_stream do render turbo_stream: turbo_stream.replace( "new_message", partial: "messages/form", locals: { message: @message } ) end format.html { render :new, status: :unprocessable_entity } end end end def destroy @message = Message.find(params[:id]) @message.destroy respond_to do |format| # Removal with animation format.turbo_stream { render turbo_stream: turbo_stream.remove(@message) } format.html { redirect_to messages_path, notice: "Message deleted" } end end end ``` ### Template Turbo Stream Chuyen Dung Doi voi cac phan hoi phuc tap hon, template `.turbo_stream.erb` mang lai su linh hoat hon. ```erb <%= turbo_stream.prepend "messages" do %> <%= render @message %> <% end %> <%= turbo_stream.update "message_count" do %> <%= Message.count %> messages <% end %> <%= turbo_stream.replace "new_message" do %> <%= render "form", message: Message.new %> <% end %> <%= turbo_stream.prepend "flash_messages" do %>
Message sent successfully!
<% end %> ``` > **Dinh Dang Phan Hoi** > > Cac phan hoi Turbo Stream phai co content-type `text/vnd.turbo-stream.html`. Rails xu ly dieu nay tu dong voi `respond_to` va dinh dang `turbo_stream`. ## Broadcasting Thoi Gian Thuc voi Action Cable Turbo Streams thuc su toa sang voi broadcasting WebSocket. Nguoi dung nhan cap nhat tuc thi ma khong can polling. ```ruby # app/models/message.rb class Message < ApplicationRecord belongs_to :room belongs_to :author, class_name: "User" # Automatic broadcast after creation after_create_commit do broadcast_append_to( room, target: "messages", partial: "messages/message", locals: { message: self } ) end # Broadcast after update after_update_commit do broadcast_replace_to( room, target: dom_id(self), partial: "messages/message", locals: { message: self } ) end # Broadcast after deletion after_destroy_commit do broadcast_remove_to(room, target: dom_id(self)) end end ``` View dang ky stream tuong ung voi helper `turbo_stream_from`. ```erb

<%= @room.name %>

<%= turbo_stream_from @room %>
<%= render @room.messages.order(created_at: :asc) %>
<%= form_with model: [@room, Message.new], id: "new_message" do |f| %> <%= f.text_area :content, placeholder: "Your message..." %> <%= f.submit "Send" %> <% end %> ``` ### Broadcasting Bat Dong Bo cho Thao Tac Nang De tranh chan cac yeu cau, broadcasting co the duoc thuc hien o che do nen. ```ruby # app/models/report.rb class Report < ApplicationRecord belongs_to :user after_create_commit :generate_async private def generate_async GenerateReportJob.perform_later(self) end end ``` ```ruby # app/jobs/generate_report_job.rb class GenerateReportJob < ApplicationJob queue_as :default def perform(report) # Simulating a long operation report.update!(status: "processing") # Notify user of start report.broadcast_replace_to( report.user, :reports, target: dom_id(report), partial: "reports/report" ) # Generate the report result = ReportGenerator.new(report).generate report.update!(content: result, status: "completed") # Notify user of completion report.broadcast_replace_to( report.user, :reports, target: dom_id(report), partial: "reports/report" ) end end ``` ## Cac Mau Nang Cao voi Turbo ### Chinh Sua Bieu Mau Inline Mot mau pho bien lien quan den viec thay the noi dung tinh bang bieu mau chinh sua inline. ```erb
<%= task.content %>
<%= link_to "Edit", edit_task_path(task) %> <%= button_to "Delete", task_path(task), method: :delete, data: { turbo_confirm: "Delete this task?" } %>
``` ```erb <%= form_with model: @task do |f| %> <%= f.text_field :content, autofocus: true %>
<%= f.submit "Save" %> <%= link_to "Cancel", task_path(@task) %>
<% end %>
``` ### Dieu Huong Ngoai Frame Cha Mac dinh, cac lien ket trong frame se o lai trong frame do. Thuoc tinh `data-turbo-frame` cho phep nham den frame khac hoac toan bo trang. ```erb ``` ```erb
<%= yield %>
``` > **Debug Turbo** > > Trong moi truong phat trien, mo console JavaScript va go `Turbo.setProgressBarDelay(0)` de thay thanh tien trinh ngay lap tuc. `Turbo.session.drive = false` tam thoi tat Turbo Drive. ## Xu Ly Loi va Trang Thai Tai UX tot doi hoi xu ly cac trang thai tai va loi mang. ```erb <%= form_with model: @model, data: { controller: "form-loading", action: "turbo:submit-start->form-loading#disable turbo:submit-end->form-loading#enable" } do |f| %> <%= yield f %> <% end %> ``` ```javascript // app/javascript/controllers/form_loading_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["submit", "text", "spinner"] disable() { this.submitTarget.disabled = true this.textTarget.classList.add("hidden") this.spinnerTarget.classList.remove("hidden") } enable() { this.submitTarget.disabled = false this.textTarget.classList.remove("hidden") this.spinnerTarget.classList.add("hidden") } } ``` ### Xu Ly Loi Server ```ruby # app/controllers/application_controller.rb class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound do |exception| respond_to do |format| format.turbo_stream do render turbo_stream: turbo_stream.replace( "main_content", partial: "shared/not_found" ) end format.html { render "errors/not_found", status: :not_found } end end end ``` ## Toi Uu Hoa Hieu Suat Nhieu ky thuat dam bao cac ung dung Turbo co hieu suat tot. ```ruby # app/controllers/messages_controller.rb class MessagesController < ApplicationController # Avoid N+1 queries with eager loading def index @messages = Message.includes(:author, :room) .order(created_at: :desc) .page(params[:page]) end # Fragment caching for static elements def show @message = Message.find(params[:id]) fresh_when @message end end ``` ```erb <% cache message do %>
<%= message.content %> By <%= message.author.name %>
<% end %> ``` ## Ket Luan Hotwire va Turbo bien doi viec phat trien Rails bang cach loai bo do phuc tap cua cac framework JavaScript truyen thong. Turbo Drive tang toc dieu huong, Turbo Frames cho phep cap nhat co muc tieu, va Turbo Streams cung cap kha nang thoi gian thuc manh me. ### Danh Sach Kiem Tra de Bat Dau voi Hotwire - Su dung Rails 7+ de tich hop Hotwire san co - Hieu ba thanh phan: Drive, Frames va Streams - Xac dinh cac vung trang se huong loi tu Turbo Frames - Su dung `respond_to` voi `format.turbo_stream` trong controller - Trien khai broadcasting cho tinh nang thoi gian thuc - Ket hop voi Stimulus cho tuong tac JavaScript don gian Cach tiep can "HTML qua day" nay cho phep xay dung cac ung dung hien dai, phan hoi trong khi duy tri su don gian va nang suat ma Ruby on Rails tro nen manh me. Ket qua: it ma nguon can bao tri hon, hieu suat tuyet voi va trai nghiem nha phat trien toi uu. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/ruby-on-rails/ruby-on-rails-7-hotwire-turbo