đź’ľ
Cheat Sheet
  • Cheat Sheet
  • Git
    • Initialize a new project
    • Make a new commit
    • Revert the most recent commit
    • Create a new branch
    • Setup terminal prompt to show the current git branch
    • Merge a branch into the current branch
    • Push a new branch up to GitHub (if it doesn’t exist there yet)
    • Pull a new branch from to GitHub to a second computer (if it doesn’t exist there yet)
    • Go back to an older commit and re-work from there
    • Delete a git branch
  • Rails
    • Setup Rails 8 w/ SQLite & Solid Cable, Cache & Queue
    • Stripe Pay Gem
      • Running Stripe Test Mode Locally
    • Setup new Rails app
      • Start a new Rails App (v8)
      • Start a new Rails App (v7)
      • Clone Instrumental Template to a new app
    • Rails 7 Turbo/Hotwire Cheatsheet
    • ActiveStorage with AWS S3
    • Live reloading with webpack-dev-server
    • Sidekiq & Redis
      • Setup Sidekiq in a Rails 8 app
      • Clear sidekiq queue
      • Start jobs worker
    • Setup Solid Queue/Cable/Cache in Rails 8 to share a single database
    • Run rspec tests
      • ZipMessage Tests Commands
      • Can't run rspec tests: ActiveRecord::StatementInvalid: PG::DuplicateTable: ERROR: relation "thing"
    • Acts As Tenant (using Rails console)
    • Reset database & re-run migrations
    • Ultrahook for testing email to app
    • Troubleshooting
      • Manage node versions using NVM
      • Webpacker bugs
      • Tailwind updates not showing
      • Fix orphaned migrations
      • rspec error: session not created: This version of ChromeDriver only supports Chrome version
      • “PG::ConnectionBad” error when running local rails server
        • Postgres not started (PG:ConnectionBad error)
    • OLD
      • Start a new Rails app (v6)
        • Install Tailwind CSS on a Rails Project
        • Install Stimulus.js on Rails
        • Install RSpec, Capybara, FactoryBot
        • Install Devise Masqerade
    • Hosting
      • Hatchbox.io + Digital Ocean
      • Fly.io rails app hosting
      • Render rails app hosting
      • Set up a Rails site on Heroku
        • Fix Sidekiq on Heroku
        • Setup app on Heroku for a Client
    • Edit Rails Credentials
    • Run subdomains on local
  • Misc
    • Terminal shortcuts
    • ngrok for tunneling
    • Set up a new Mac
    • Mailcatcher
  • Statamic
  • Forge / Statamic
    • Run Statamic (for Clarityflow) locally
    • Set up to SSH into Forge server
    • Production deploy failed. Resolve by SSH & resolve git conflicts.
  • Old (not using anymore)
    • Heroku
      • Push to Heroku
        • Can't push to heroku. Updates were rejected because the remote contains work you dont have locally
      • Create a Heroku App from command line
      • Set up remotes for staging and production apps
      • Run the Rails Console on Heroku
      • Migrate database on Heroku
      • Make database backups
      • Restart Heroku Dynos
      • Point a root domain (non-subdomain) to heroku
    • Middleman
      • Start a new middleman site from my template
    • Jekyll
      • Start a new Jekyll site from my template
      • Run and work on my Jekyll site
    • Netlify
      • Setup a Jekyll site on Netlify
      • Push to Staging & Production on Netlify
    • Laravel
      • Start a new Laravel app
      • Setup a Mac for running Laravel
    • Design
      • Icomoon Fonts in Rails
    • Sync Sublime Text settings across macs
    • Run a rails project on a new mac for the first time
    • Customize terminal prompt for bash
  • Cursor
    • .cursorrules for Rails + Linear issue writing
  • Mac Environment
    • Install Homebrew for both arm64 and x86
    • Installing Ruby
    • Customize terminal prompt for zsh to show git branch
    • Open terminal in arm64 or Rosetta mode
    • Install Homebrew
Powered by GitBook
On this page
  • A note about dom_id
  • Button to create a 'new' task
  • Regular links that reside inside turbo_frames (link to show task)
  • Delete a task
  • Creating a task
  • Adding a "Cancel" button
  1. Rails

Rails 7 Turbo/Hotwire Cheatsheet

My simple tutorial to remember how to wire up a basic SPA CRUD.

PreviousClone Instrumental Template to a new appNextActiveStorage with AWS S3

Last updated 1 year ago

The following is based on my takeaways from this tutorial:


Example: My app is simply a list with Tasks. I want to create a single page app where I can:

  • Create new tasks

  • see the list of tasks

  • delete tasks from the list

  • in-line edit tasks on the list

  • Click "cancel" to cancel creation or editing of a task

  • mark tasks as complete or incomplete

Basic setup:

  • Create a scaffold for Tasks, each has a title and completed boolean

  • Change the root route to point to the Tasks index view. This will be our one page for our SPA.


A note about dom_id

# If the task is persisted and its id is 1:
dom_id(@task) # => "task_1"

# If the task is a new record:
dom_id(Task.new) # => "new_task"

# Note that the dom_id can also take an optional prefix argument
# We will use this later in the tutorial
dom_id(Task.new, "prefix") # "prefix_new_quote"

That will be useful when giving turbo_frame_tags their IDs, which need to match a specific Task ID or "new_task". See below...

Button to create a 'new' task

Near the top of the index view, we will have a button, "New task", and a place where we want to render the new task form when user clicks "New task".

There are 2 ways we can structure this:

Option A: Replace the container of "New task" button with the form

Tasks controller index action should be structured like this:

  • be sure to define @task = Task new (this makes @task = "new_task" for the new task form).

def index
    @tasks = Task.all
    @task = Task.new
  end

Structure the index view like this.

  • Wrap the new task button in <%= turbo_frame_tag "task_form" do %>

  • The new task button simply points to new_task_path

<div class="">
  <%= turbo_frame_tag @task do %>
    <div class="">
      <h1 class="">Tasks</h1>

      <div>
        <%= link_to new_task_path,
                    class: "btn" do %>
          New task
        <% end %>
      </div>
    </div>
  <% end %>

  <div id="tasks" class="min-w-full space-y-4">
    <% @tasks.each do |task| %>
      <%= render task %>
    <% end %>
  </div>
</div>

Structure new.html.erb like this:

  • This view must contain a <%= turbo_frame_tag id="task_form" do %>...

<div class="">
  <h1 class="">New task</h1>

  <%= turbo_frame_tag @task do %>
    <%= render "form", task: @task %>
  <% end %>
</div>

When "New task" is clicked, its container (the turbo_frame_tag) will be replaced by the turbo_frame_tag found on the new.html.erb view (the form).

Option B: Target a place elsewhere on the page to render the form

Structure index.html.erb like this:

  • The "New Task" button has a data attribute data: { turbo_frame: "task_form" } which targets that turbo_frame_tag elsewhere on this page.

  • We place <%= turbo_frame_tag "task_form" %> in the spot where we want to render the task form.

<div class="">
  <div class="">
  <h1 class="">Tasks</h1>

    <div>
      <%= link_to new_task_path,
                  class: "btn",
                  data: { turbo_frame: "new_task" } do %>
        New task
      <% end %>
    </div>
  </div>
  
  <%= turbo_frame_tag @task %>

  <div id="tasks" class="min-w-full space-y-4">
    <% @tasks.each do |task| %>
      <%= render task %>
    <% end %>
  </div>
</div>

The new.html.erb is the same as above.

Now when user clicks "New task", it will render the new task form below where the button is. The button will remain visible.


Regular links that reside inside turbo_frames (link to show task)

Let's say we want to link the task title to the task show view.

Since the show view doesn't contain a turbo_frame_tag that matches the containing turbo_frame_tag of each task, we need to target "_top" for the link that points to the show view.

In _task.html.erb I implemented the link that points the task title to the task show view like this:

<%= link_to task_path(task),
            data: { turbo_frame: "_top" } do %>
  <%= task.title %>
<% end %>


Delete a task

We'll use turbo_streams for this.

This is the delete button inside each _task.html.erb:

<%= button_to task,
              method: :delete,
              class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" do %>
    <span>
       Delete
    </span>
  <% end %>

The destroy action in the tasks controller should look like this:

def destroy
  @task.destroy!

  respond_to do |format|
    format.turbo_stream
  end
end

Create the file destroy.turbo_stream.erb with this content:

<%= turbo_stream.remove @task %>

Now when you click the "Delete" button, task is removed from the DOM and deleted.


Creating a task

The create action in tasks controller should be this:

def create
  @task = Task.new(task_params)

  respond_to do |format|
    if @task.save
      format.turbo_stream
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

Create file create.turbo_stream.erb with this contents, which does this following:

  • Prepends the new task inside the dom element with ID "tasks"

  • Replaces the "new_task" form with the tasks form partial, and instantiates a new task by passing local variable: Task.new

<%= turbo_stream.prepend "tasks", @task %>
<%= turbo_stream.replace "new_task", partial: "tasks/form", locals: { task: Task.new } %>

Alternatively, we can use this block syntax to render the new task inside the "tasks" container:

<%= turbo_stream.prepend "tasks" do %>
  <%= render "tasks/task", task: @task %>
<% end %>

<%= turbo_stream.replace "new_task" do %>
  <%= render "tasks/form", task: Task.new %>
<% end %>


Adding a "Cancel" button

Inside the _form.html.erb partial, simply add the cancel link like this:

<%= link_to "Cancel", tasks_path, class: "" %>
  • That points to the tasks index view (index.html.erb)

  • When user is in the new task form, then it will replace the "new_task" turbo_stream_tag with the contents of the "new_task" turbo_stream_tag found in index.html.erb, which has no content. So it (correctly) replaces it with nothing.

  • When user is in the editing task form on any specific task, it will target that task's turbo_stream_tag (task_id) and replace that with the contents of the the same turbo_stream_tag (task_id) found on index.html.erb which is the content of this task.

Learn how to use Turbo Frames and Turbo Stream templatesLearn how to use Turbo Frames and Turbo Stream templates
Logo