Abram Abram - 5 months ago 11
Ajax Question

AJAX form (using simple_form) with preserved error validation

This might be one of the more difficult questions I've asked, but I have been stuck for a few days, and I thought it'd be best to get support from the more experienced rails community.

I am working on a dummy app that follows this guide roughly... ajax/jquery tutorial...

Making my own customizations, the guide helped me to create a "brand" (includes nested models "model", "submodel", and "style") submission form (using simple_form) and brands listing on the same page.. This works perfectly, and the validation (for my models) is enforced.. However, when I put the form and brand listing on the same page I lose the neat inline validation errors that were appearing on submission failure (see image below). Not being able to get the inline errors to work has made me realize that I have more to learn about rendering.. and that is why I am fixated on finding the answer to this question

enter image description here

...and here is the listing:

enter image description here

Below is the controller for the index action:

def index

#This is for the product listing

@brands = Brand.all.reverse

#This is for the form

@brand = Brand.new
@model = Model.new
@submodel = Submodel.new
@style = Style.new

respond_to do |format|

The above form creates the brand, model, submodel, and style for use in the nested submission form... Below is the code for the form:

<%= simple_form_for @brand, :html => { :class => 'form-horizontal' }, :remote => true do |m| %>

<fieldset style = "margin-top:34px;">
<%= m.input :name, :label => 'Brand' %>
<%= m.simple_fields_for :models, @model do |p| %>
<%= p.input :name, :label => 'Model' %>
<%= p.simple_fields_for :submodels, @submodel do |s| %>
<%= s.input :name, :label => 'Submodel' %>
<%= s.simple_fields_for :styles, @style do |ss| %>
<%= ss.input :name, :label => 'Style' %>
<% end %>
<% end %>
<% end %>

<div class="form-actions">
<%= m.submit nil, :class => 'btn btn-primary' %>
<%= link_to 'Cancel', brands_path, :class => 'btn' %>
<% end %>

Now as you can see I am using
:remote => true
... I would like for the form to either create the new "brand", or for the form to reload with inline validation errors. At the moment my create action looks like:

def create
@brand = Brand.new(params[:brand])

respond_to do |format|
if @brand.save
format.html { redirect_to brands_url, notice: 'Brand was successfully created.' }
format.json { render json: @brand, status: :created, location: @brand }
format.html { render action: "index" }
format.json { render json: @brand.errors, status: :unprocessable_entity }
format.js { render 'reload' }

The code that appears under
if @brand.save
seems to work.. but the code below "else" doesn't work the way I'd like. So what is happening when @brand does NOT save? I believe that the code within the index action is being run, then @brand.errors is converted to json (which I assume is for the simple_form validation errors), and then reload.js.erb is being run..

In an attempt to reload the form (with validation errors) I have put the line
$("#entryform").load(location.href+" #entryform>*","");
into reload.js.erb ... When I put invalid data into my form reload.js.erb is being called, but all that happens is that the form fields reload blank, instead of having the data entered and inline validation errors.

I hope I have provided enough info here to get help.. Really struggling on this one. Thanks!


I think you can simplify this by putting your form in a partial (we'll call it simple_form), and putting this code in reload.js.erb:

$("#entryform").html("<%= escape_javascript(render :partial => 'simple_form') %>");

I'm not familiar with jQuery's load method, but I assume it does a second request. The key with rendering errors is the instance variable (@brand) you're using while rendering the form has to be the same one you tried to save in create, so you can check the errors on it. This is why you always use render instead of redirect_to when you want to render errors. If you do a redirect, you start a new request, meaning @brand is reinitialized. I'm assuming your $(...).load(...) is having the same issue.