Skip to content

Blogging On Rails

Everything on Rails!

Filtering Data in HOTWire

When we have a long list of Todos, sometimes we want to filter them by name. We can easily do this using Turbo’s morphing and a Stimulus controller to update the page from the server.

One previous way to get this interactivity was to use a Stimulus controller that filtered the HTML. This still works, and might be a strategy depending on your situation. This technique will send the request to the server, and leverage the Database to perform the filtering. This might work better if you have pagination, or don’t want to load hundreds or thousands of records onto a page to perform filtering.

Filtering on the Server

Start by updating todos_controller.rb and the index action. The controller should have default query of the Todos:

@todos = Todo.all.order("priority")

We will look for a query parameter called :todo that we’ll use to filter the name of the Todo. The action checks for the presence of the parameter, and that it isn’t blank. It lowercases the param, and then performs a lower case query of all the names in the Todo. If the todo parameter is missing, it pulls in all the Todos. It then sorts the values by priority.

    query = if params[:todo] && !params[:todo].nil? && !params[:todo].blank?
      name = "%#{params[:todo].downcase.strip}%"
      Todo.where("lower(name) like ?", name)
    else
      Todo.all
    end
    @todos = query.order("priority")

The Stimulus Search controller

You can generate a new controller we’ll call search:

$ ./bin/rails g stimulus search

This controller will have a parameter value for the url to visit, and a single action, search. When the search action triggers, it will read the name and value from the target that fired the event, append that to the urlValue, and tell Turbo to visit the page. Turbo will make the request, and perform the morphing.

import { Controller } from "@hotwired/stimulus";

// Connects to data-controller="search"
export default class extends Controller {
  static values = { url: String };

  search(event) {
    Turbo.visit(`${this.urlValue}?${event.target.name}=${event.target.value}`);
  }
}

Wiring up the HTML

First, we can add an input field for our search controller.

  <input 
    id="search" 
    type="text" 
    name="todo" 
    value="<%= params[:todo] %>"
    placeholder="Search" 
    data-controller="search" 
    data-search-url-value="<%= request.path %>"
    data-action="search#search"
    data-turbo-permanent
    class="w-full my-1 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6">    
  <!-- existing todos div -->

The action will call to our Stimulus controller, and fetch the updated results. The form gets set to whatever was passed, so that the page refreshes have the correct information in the search field. The input field is also set as data-turbo-permanent so that it keeps focus when the server results come back, and the page is morphed. If it wasn’t there, the text field would lose focus after every keystroke, which provides for a poor user experience.

Now you have a simple way to filter data on your page.

Make Interactivity Default 

Make your web app interactive now with easy to implement and simple to add HOTWire integrations. 

Enter your email and get a free sample of my HOTWire Tutorials ebook.

We won’t send you spam. Unsubscribe at any time.

Leave a Reply

Your email address will not be published. Required fields are marked *