Hotwire Turbo Replacing Rails UJS

Episode #307 by David Kimura

Summary

In this episode, we look at some common functionality that we got with Rails UJS and what it looks like to reimplement these with Hotwire's Turbo and StimulusJS.
hotwire javascript rails stimulusjs turbo ujs 9:36

Resources

Download Source Code

Summary

# Terminal
rails new template --skip-javascript
bin/rails g scaffold products name color "price:decimal{8,2}" sku
bundle add faker
bundle add hotwire-rails
bin/rails hotwire:install

# db/seeds.rb
100.times do
  Product.create(
    name: Faker::Lorem.word,
    color: Faker::Color.hex_color,
    price: Faker::Commerce.price,
    sku: Faker::Number.number(10)
  )
end

# views/products/index.html.erb
<% @products.each do |product| %>
  <%= content_tag :tr, id: dom_id(product) do %>
    <td><%= product.name %></td>
    <td><%= product.color %></td>
    <td><%= product.price %></td>
    <td><%= product.sku %></td>
    <td><%= link_to 'Show', product %></td>
    <td><%= link_to 'Edit', edit_product_path(product) %></td>
    <td><%= link_to 'Destroy', product,
              data: {
                "turbo-method": :delete,
                controller: "confirmation",
                "confirmation-message-value": 'Are you sure?',
                action: "confirmation#confirm"
              } %></td>
  <% end %>
<% end %>

# OPTIONS 1 - products_controller.rb
include ActionView::RecordIdentifier

def destroy
  @product.destroy
  respond_to do |format|
    format.html { redirect_to products_url, notice: "Product was successfully destroyed." }
    format.json { head :no_content }
    format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@product)) }
  end
end

# OPTION 2 - products_controller.rb
def destroy
  @product.destroy
  respond_to do |format|
    format.html { redirect_to products_url, notice: "Product was successfully destroyed." }
    format.json { head :no_content }
    format.turbo_stream {}
  end
end

# OPTION 2 - views/products/destroy.turbo_stream.erb
<%= turbo_stream.remove(dom_id(@product)) %>

# app/assets/javascripts/controllers/confirmation_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static values = { message: String }

  confirm(event) {
    if (!(window.confirm(this.messageValue))) {
      event.preventDefault()
      event.stopImmediatePropagation()
    }
  }
}