Clear input field in Rails search form
We are building a simple feature to clear the input field from a search form. Often I have seen implemented a “Clear” button that does a simple request to clear the parameters and reset the search results. However, I like the results from my previous search to remain while I make a new search. So we are going to use a bit of Stimulus to implement this feature in our Rails search form with a bit of TailwindCSS for styling.
The code
bin/rails g stimulus clear_form
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="clear-form"
export default class extends Controller {
static targets = [ "input", "clearButton" ]
connect() {
this.toggleClearButton();
console.log("ClearForm controller connected")
}
toggleClearButton() {
if (this.inputTarget.value) {
this.clearButtonTarget.style.display = "inline";
} else {
this.clearButtonTarget.style.display = "none";
}
}
clearInput() {
this.inputTarget.value = "";
this.inputTarget.dispatchEvent(new Event('input'));
this.toggleClearButton();
}
inputChanged() {
this.toggleClearButton();
}
}
<%= form_with url: listings_path, method: :get, local: true do %>
<div class="relative" data-controller="clear-form">
<%= text_field_tag :query, params[:query], data: { clear_form_target: "input", action: "input->clear-form#inputChanged" }, id: "searchInput", placeholder: "Search listings...", class: "border p-1 rounded-sm pr-6 w-full" %>
<%= button_tag type: "button", data: { clear_form_target: "clearButton", action: "click->clear-form#clearInput" }, class: "cursor-pointer absolute top-1/2 right-2 -translate-y-1/2 hidden" do %>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
<% end %>
</div>
<%= submit_tag "Search", class: "bg-stone-400 text-stone-100 p-1 rounded-sm" %>
<% end %>
def index
return @listings = Listing.all unless params[:query].present?
@listings = Listing.search(params[:query])
end
<% if @listings.present? %>
<div class="grid grid-cols-12 gap-4">
<% @listings.each do |listing| %>
<div class="col-span-6 md:col-span-3">
<%= render ListingCardComponent.new(listing:) %>
</div>
<% end %>
</div>
<% else %>
<div>There are no listings yet</div>
<% end %>
The explanation
We start off by generating the Stimulus controller to handle all the JavaScript logic.
Using Stimulus will allow us to reuse this code on different forms by allowing us
to add the data
attributes to the HTML instead of our JavaScript looking for a specific
id
value on the HTML elements.
In our Stimulus controller we set up the targets on the HTML elements we want to focus on.
In this case it is just the input search field and the span
tag that will be the button.
As soon as our controller connects to the page it runs toggleClearButton()
which checks to see
if there is a value in the input field. If there is then display the button inline, hide
if not.
The inputChanged()
function runs when a user types in the field as indicated by the data-action
attribute on the form’s HTML. Now when there is a value that exists in the input field, the button
appears and when you click it will clear the form by setting the value to ""
.
The ListingsController
returns the searched query in the index
action or all listings if
there isn’t a term in the parameters. The listings are rendered by the following HTML/ERB code.