Nikita Kazakov
Nikita Kazakov
5 min read

Tags

Sometimes you want to update a form and not refresh the page. This is where AJAX (Asynchronous Javascript) comes handy.

Instead of the controller responding with an html page, we’ll have the controller respond with Javascript and update the page.

Let’s create a list of restaurants by creating a Rails model with restaurant name and category.

rails generate model restaurant name:string category:string

I’m going to add a few restaurants to the database by going into the console.

rails c
Restaurant.create(name:"Burger King", category:"Fast Food")
Restaurant.create(name:"Cheese Cake Factory", category:"Family")

Let’s generate a rails controller.

rails g controller restaurants

We’ll generate the standard routes for our restaurant resource in routes.rb

Rails.application.routes.draw do
  resources :restaurants
end

I’m going to be using HAML gem instead of ERB to build out the html. If you need to see what’s going on in erb, feel free to use the haml to erb converter.

Let’s add the index action to the restaurants controller and load all of the restaurants into the @restaurants instance variable. @restaurants is now available for use in our views.

class RestaurantsController < ApplicationController
  def index
    @restaurants = Restaurant.all
  end
end

I’ll create an index view views/restaurants/index.html.haml. I’ll add a simple table. Notice that I’m using Bootstrap 4 for styling but it’s completely optional.

%table.table.table-hover
  %thead
    %tr
      %th Name
      %th Category
  %tbody
    %tr
      %td</pre><figure class="wp-block-image size-large">

center-aligned-image

We have a table but there’s nothing in it. Let’s add the list of restaurants…

%table.table.table-hover
  %thead
    %tr
      %th Name
      %th Category
  %tbody
    - @restaurants.each do |restaurant|
      %tr
        %td
          = restaurant.name
        %td
          = restaurant.category

</figure>

Let’s add a link to the index page to add a new restaurant.

= link_to "Add Restaurant", new_restaurant_path, class:"btn btn-primary", remote: true

</figure>

Notice that I added remote: true to the link_to helper. Statement tells Rails we’re going to be responding with javascript and there’s no reason to redirect to another page when the button is clicked.

Go ahead, try to click the button. Nothing will happen.

Ajax and responding with JS

Let’s create a new action in the controller and load a new restaurant instance into the @restaurant instance variable. We’re going to use this in our form.

class RestaurantsController < ApplicationController
  def index
    @restaurants = Restaurant.all
  end
  def new
    @restaurant = Restaurant.new
  end
end

Instead of clicking on the Add Restaurant button and going to a new page, we want the form to be rendered on the same page. This means that the new action should NOT respond to html. It should instead respond to js.

I’m using the responders gem to define how an action in the controller should respond. One line 2 I state that the new action should respond to js instead of html.

class RestaurantsController < ApplicationController
  respond_to :js, only: [:new]
  def index
    @restaurants = Restaurant.all
  end
  def new
    @restaurant = Restaurant.new
  end
end

Let’s return back to the add restaurant button. When you click it, watch your server console. You’ll see that Rails is trying to process the new action as JS but it can’t find a template to use.

</figure>

We solve this by creating new.js.erb under views/restaurants.

Refresh your page and click the add restaurants button again. The new.js.erb file renders but it’s empty.

</figure>

Notice that this file is first processed by erb (ruby) and then by JavaScript. It’s a JavaScript file. I know that I’m using HAML but why would I want to process this with erb and then js? Because Ruby Mine won’t do syntax highlighting if I process it as new.js.haml. That’s the only reason.

We want this file to display a form for us to add new restaurants.

Let’s add a form to our index page that we’ll use to add a new restaurant. I’ll use form_with and call on a new instance of a Restaurant. I added the form right above the link_to helper. Take note that I added an id new-restaurant-form so that I had a way of selecting the form somehow with java script.

%h1 Restaurants
%table.table.table-hover
  %thead
    %tr
      %th Name
      %th Category
  %tbody
    - @restaurants.each do |restaurant|
      %tr
        %td
          = restaurant.name
        %td
          = restaurant.category
%div#new-restaurant-form.my-3
  =form_with model: Restaurant.new do |form|
    = form.text_field :name, placeholder: 'Name'
    = form.text_field :category, placeholder: 'Category'
    = form.submit
= link_to "Add Restaurant", new_restaurant_path, class:"btn btn-primary", remote: true

Let’s create create action that will get triggered once the “create restaurant” button is clicked. Let’s also create strong parameters as a private method which helps us create the new restaurant object. In restaurants_controller.rb, add:

  def create
    @restaurant = Restaurant.new(restaurant_params)
    @restaurant.save
  end

  private

  def restaurant_params
    params.require(:restaurant).permit(:name, :category)
  end

Note that in the create action, we’re grabbing the passed in params from the form we submitted and we’re filling out a new instance of @restaurant. We’re then saving the restaurant object to the database.

Go to the restaurants page and enter a restaurant and click the “create restaurant” button. Nothing happens. Refresh the page manually and you’ll see your entry in the list.

This is not quite what we want. Let’s add some magic using Javascript (Jquery in this case).

</figure>

First I want the form to appear when we click the add restaurant button. By default, I’m going to apply an HTML class d-none(display: none) to hide the form by default. When the page is refreshed, the form is not visible.

%div#new-restaurant-form.my-3.d-none

When we render new.js.erb, I want to show the form. I can do that using Jquery. Inside of new.js.erb add:

$('#new-restaurant-form').toggleClass('d-none')

When the new.js.erb action is run, we select the new-restaurant-form by id using Jquery and toggle the class to remove display: none. Refresh the page and try to press the add restaurant button. You’ll see that the form show up and disappears each time you press the button.

However, we want to see the restaurant added to the table.

Let’s dynamically add a row in our create.js.erb file. We’re calling the @restaurant instance variable and grabbing it’s name and category and inserting those values into the table as a row.

$('tbody').after('<tr class="child"><td><%= j(@restaurant.name) %></td><td><%= j(@restaurant.category) %></td></tr>')

Try to fill out the form now. You’ll see that the table updates automatically.