Using form_tag and params to create a search or render a sorted view in Rails

Tracie Masek
5 min readSep 4, 2019

--

Let’s say you want your user to be able to change the way information is presented on a particular page in your Rails app. Perhaps you’d like to give your user the ability to sort the data by a certain object attribute. Or maybe you’ve got a massive list of items on your index page, and you’d like your user to be able to search for a string, and return all the items on the index page that have an attribute that matches the search string.

Sure, you could create a new route, and a new controller action and send the user to a new page with the results of their search or sort, but there is another way.

You can use form_tag to get the appropriate user input through the params hash when the form is submitted, and then incorporate the value of the params into some logic in your controller that will pass the altered data to the view to render. (You can do the same thing with button_to, but I'll use form_tag in this example since it works better for the search and it'll keep the examples a little more consistent.)

Sort option

For this example, we have a bunch of adopters and a bunch of pets. A pet belongs_to an adopter, and an adopter has_many pets. On our adopter index page that lists all the adopters, we want our user to have the option to sort the adopters by the number of pets that belong to them, in descending order. The top of the list should be the adopter with the most pets.

The View

Let’s make a quick form with a submit button in the adopters index.html.erb view file:

#apps/views/adopters/index.html.erb1  <%= form_tag adopters_path, method: :get do %> 
2 <%= submit_tag “Sort by number of pets”, name: :sort %>
3 <% end %>

A few things to note: make sure to use the output printing ERB tags(<%= %>)for the Ruby form_tag. I’m using URL route helper (adopters_path) rather than the hard coded path (“/dogs”). form_tag’s method defaults to POST, so you’ll need to add method: :get to override the default value.

I’m also overriding the standard name attribute that is sent with the params when the form is submitted. It’s normally sent in the params hash with the key “commit,” which we could work with if we had to, but it makes it a little bit more clear to rename it something aligning with the action the user would like to see happen.

How do I know that the form usually sends a param with the key of “commit”? Because I inspected the heck of the params using byebug.

To inspect the params, have your local server booted up and refreshed on the view page that the submit form button is on. Then put a byebug in the appropriate controller action (the one the request’s path is set to), and click the button you’re testing in the rendered view. (Since our form submit is sending a GET request to the index action, we’ll put the byebug there.) Once you click the button in the browser window, you should hit the byebug in your terminal console, and then type params into the console to see what they are:

1: class AdoptersController < ApplicationController
2: def index
3:
4: byebug
=> 5: @adopters = Adopter.all
6:
7: end
8:
9:
10:
(byebug) params
<ActionController::Parameters {"utf8"=>"✓", "commit"=>"Sort by dog with the most employees", "controller"=>"dogs", "action"=>"index"} permitted: false>

By adding name: :sort to the form_tag options, we can change the name of the param from “commit” to “sort”.

The Controller

Now that we better understand what params are being sent with our request, we can use them to add some logic to the controller:

1  class AdoptersController < ApplicationController
2 def index
3 if params[:sort]
4 @adopters = Adopter.all.sort_by do |adopter|
5 adopter.pets.count
6 end.reverse
7 else
8 @adopters = Adopter.all
9 end
10 end

If the GET request is sent without a key of “sort” in the params, the index controller will pass the unsorted instance variable that gets all the dogs from the Dog model class. If it receives the “sort” param, it’ll send a different instance variable to the view to be rendered. In this case it’ll send the adopters sorted by the number of pets that belong to them (using ActiveRecord and other methods/enumerables).

This also works with button_to, just make sure to update the method to :get, and to add the secret “sort” param to name:

<%= button_to “Sort by number of pets”, adopters_path, method: :get, name: :sort%>

A Simple Search using form_tag

We can reuse the concepts and some of the code from above to instead search our index list and return only the objects that meet our search criteria. To keep things simple (this is meant to be a very basic beginner approach to building a search — we ain’t building google here), we’ll direct our users in their search with a form label instructing them what to search for. In this case, they can search the list of dogs by name.

The View

In the index view file for dogs, we’ll make another form_tag:

#apps/views/dogs/index.html.erb1  <%= form_tag dogs_path, method: :get do %>
2 <%= label_tag "Search for a doggo by name:" %>
3 <%= text_field_tag :search, params[:search] %>
4 <%= submit_tag ‘Search’, name: nil %>
5 <% end %>

It’s fairly similar to the form_tag button we made to sort, with a few things added. In line 2, we’re adding a label_tag to instruct the user to search for a dog by name. In the text_field_tag, we’re once again re-naming the params key, this time to “search” to make it a little more obvious. Adding , params[:search] to the text_field_tag means that the value of the string that the user inputs will show in the form when the page is reloaded with the searched for items. In line 4, adding name: nil will remove commit=Search from the URL when the user hits the Search button.

The Controller

In the dogs_controller index action, add the logic to handle the search:

1  class DogsController < ApplicationController
2 def index
3 if params[:search]
4 @dogs = Dog.where(‘name LIKE ?’, “%#{params[:search]}%”)
7 else
8 @dogs = Dog.all
9 end
10 end

In the index action, we’ll include an if statement to check for params with the key “search” and if they are present, the controller will send a different collection of dogs to the view page to be rendered. If the “search” params aren’t present, it’ll send the standard collection of all the dogs.

Calling .where (an ActiveRecord finder method) like this will allow a more forgiving search. It won’t matter if the user capitalizes the search string, or misses a letter. If you want your use to have to type in a name exactly as it is in the database, you can rewrite it:

@dogs = Dog.where(‘name = ?’, params[:search])

And there you have it! A way to use form_tag to give the user some simple ways to interact with the data in your project!

--

--