Ruby on Rails 4 AJAX Form Example

In this quick post I'll share some code to submit a Ruby on Rails 4 model form via AJAX.

I created a new directory and added RVM files for Ruby version and gemset.

File: .ruby-version

ruby-2.1.0

File: .ruby-gemset

rails4ajax

Installed Rails gem

gem install rails

Created a new Rails project

rails new rails4ajax

Generated the complete scaffolding for a Model called "Thing".

rails generate scaffold Thing title:string

Set up database (defaults to SQLite)

rake db:migrate

Added model validation, to test the ajax error response.

# file: app/models/thing.rb:

class Thing < ActiveRecord::Base
  validates :title, presence: true
end

Revised the json format blocks for the update and create actions in the Things controller.

# file: app/controllers/things_controller.rb

@@ -29,10 +29,10 @@ class ThingsController < ApplicationController
     respond_to do |format|
       if @thing.save
         format.html { redirect_to @thing, notice: 'Thing was successfully created.' }
-        format.json { render action: 'show', status: :created, location: @thing }
+        format.json { render json: @thing }
       else
         format.html { render action: 'new' }
-        format.json { render json: @thing.errors, status: :unprocessable_entity }
+        format.json { render json: @thing.errors.full_messages, status: :unprocessable_entity }
       end
     end
   end
@@ -43,10 +43,10 @@ class ThingsController < ApplicationController
     respond_to do |format|
       if @thing.update(thing_params)
         format.html { redirect_to @thing, notice: 'Thing was successfully updated.' }
-        format.json { head :no_content }
+        format.json { render json: @thing }
       else
         format.html { render action: 'edit' }
-        format.json { render json: @thing.errors, status: :unprocessable_entity }
+        format.json { render json: @thing.errors.full_messages, status: :unprocessable_entity }
       end
     end
   end

Revised the form, modified the form_for tag and error container html

# file: app/views/things/_form.html.erb

<%= form_for(@thing, remote: true, format: :json, html: {class: :thing_form}) do |f| %>

  <div id="error_explanation" style='display:none;'>
    <ul>
      <% if @thing.errors.any? %>
        <% @thing.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      <% end %>
    </ul>
  </div>

  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Added AJAX listeners (coffeescript)

# file: app/assets/javascripts/things.js.coffee

$(document).ready ->

  $(document).bind "ajaxSuccess", "form.thing_form", (event, xhr, settings) ->
    $thing_form = $(event.data)
    $error_container = $("#error_explanation", $thing_form)
    $error_container_ul = $("ul", $error_container)
    $("<p>").html(xhr.responseJSON.title + " saved.").appendTo $thing_form
    if $("li", $error_container_ul).length
      $("li", $error_container_ul).remove()
      $error_container.hide()

  $(document).bind "ajaxError", "form.thing_form", (event, jqxhr, settings, exception) ->
    $thing_form = $(event.data)
    $error_container = $("#error_explanation", $thing_form)
    $error_container_ul = $("ul", $error_container)
    $error_container.show()  if $error_container.is(":hidden")
    $.each jqxhr.responseJSON, (index, message) ->
      $("<li>").html(message).appendTo $error_container_ul

I started the server via "rails s", and browsed to the url: http://localhost:3000/things/new

Submitting the form without entering a title will show the errors container. Submitting with a title will hide the errors and display a message that the Thing was saved.

Source code on Github