Sample dropdown ajax with rails

Intro

If you haven't done ajax before. This article is all you need. This sample uses ajax with ruby on rails (server)

Problems

Imagine you have a form which has 2 dropdowns

  • Country
  • City

A country has many cities and the city must be belongs to a country. Problem here, when you select the country, you want the city must be dynamic follow the country.

Let's do it

Create 2 models country and city with the relationship 1-n This sample builds an user register form 2 fields country and city.

Create a form with 2 dropdowns

On controller:

@user = User.new
@countries = Country.all
@cities = []

On the view

div.col-md-6.col-md-offset-3
  = form_tag new_user_path do
    #country.form-group
      = select_tag :country, options_for_select(@countries.collect{|p| [p.name, p.id]}, selected: params[:country]),
        prompt: "Select country", class: "form-control"
    #city.form-group
      = select_tag "city", options_for_select(@cities.collect{|p| [p.name, p.id]}, selected: params[:city]),
        prompt: "Select city", class: "form-control"

That's all for preparing. Let's do ajax now

The theory

AJAX is very simple. STEP 1. Clients send a request to server. STEP 2. Server receives the request, then calculates and sends the results to clients. STEP 3. Client receives the response. And do anything you want (replace the views, show alert...)

Practice step by step

STEP 1: Client sends a request to server.

Sample code

$(document).on("change", "#country select", function(){
  var country = $(this).val();
  $.ajax({
    url: "/users/new",
    method: "GET",  
    dataType: "json",
    data: {country: country},
    error: function (xhr, status, error) {
      console.error('AJAX Error: ' + status + error);
    },
    success: function (response) {
      console.log(response);
    }
  });
});

Firstly, you catch the event, here's on change dropdown.

Then do the ajax simply with some infos: url(target address), method(action method), data type (type of data response) and data (client sent to server) The success and error are the results when ajax completes (success or failure). They are step 3.

STEP 2: Server receives the request, then calculates and sends the results to clients.

Now, the server receives the data sent from clients (when dropdown country changed) and it must be processed with that data. Here, clients send the country_idto server, and want to get the city list. Add the follow code to controller

if params[:country].present?
  @cities = Country.find(params[:country]).cities
end
if request.xhr?
  respond_to do |format|
    format.json {
      render json: {cities: @cities}
    }
  end
end

It just finds the cities corresponding to the country, then sends back to the client.

The full controller

@user = User.new
@countries = Country.all
@cities = []
if params[:country].present?
  @cities = Country.find(params[:country]).cities
end
if request.xhr?
  respond_to do |format|
    format.json {
      render json: {cities: @cities}
    }
  end
end

STEP 3: Client receives the response then builds the dropdown city.

Back to javascript.

Now, if ajax success, you can see the respone by console.log(respone). All you need to do now is build a cities dropdown from the response. Correct your js like bellow:

success: function (response) {
  console.log(response);
  var cities = response["cities"];
  $("#city select").append('<option>Select city</option>');
  for(var i=0; i< cities.length; i++){
    $("#city select").append('<option value="' + cities[i]["id"] + '">' + cities[i]["name"] + '</option>');
  }
}

Great! That works. Is it done? Not yet. Look, your city dropdown. It has 2 items select city. And plz change the country and look at the city. What's that? Don't worry. It seems you are not clear the dropdown after select other country. Very simple, just add bellow code before you build dropdown city

$("#city select").empty();

The complete javascript

$(document).on("change", "#country select", function(){
  var country = $(this).val();

  $.ajax({
    url: "/users/new",
    method: "GET",
    dataType: "json",
    data: {country: country},
    error: function (xhr, status, error) {
      console.error('AJAX Error: ' + status + error);
    },
    success: function (response) {
      console.log(response);
      var cities = response["cities"];
      $("#city select").empty();

      $("#city select").append('<option>Select city</option>');
      for(var i=0; i< cities.length; i++){
        $("#city select").append('<option value="' + cities[i]["id"] + '">' + cities[i]["name"] + '</option>');
      }
    }
  });
});

What should I do more? Hmm! May be your ajax works fine now. But in case of server's busy and takes more time to response. User can't know ajax is being processed.

You should let user know that. Very simple, just add an image (*.gif) when ajax starts, and hide it when ajax is done (error or success)

That's your exercise!

That's all

Happy coding!!!!

All Rights Reserved