In a previous post, Rails 3, jQuery & UJS, I talked about how easy Rails has made it to do remote forms or even linking to remote actions. It covered how to set up the basics of a remote form to gather the information and let the user know something is being processed. The only question I didn’t cover is, what happens when a form returns a validation error? It’s as easy as making a generic js.erb file.
The setup
Assuming that you already have your UJS working to save the data when everything validates, let’s setup a way to handle when the data does not validate. I will just be covering the example of creation, but this can easily be applied to the update action as well.
The controller
1 | def create |
2 | @bar = Foo.new(params[:foo]) |
3 | respond_to do |format| |
4 | if @bar.save |
5 | format.html { } #index.html |
6 | format.js { } #create.js.erb |
7 | else |
8 | format.html { } #new.html |
9 | format.js { |
10 | render :template => "shared/ujs/form_errors.js.erb", |
11 | :locals => { |
12 | :item => @bar, |
13 | :notice => 'There were errors creating your bar.’ |
14 | } |
15 | } |
16 | end |
17 | end |
18 | end |
The important part of this is the handling of when it doesn’t save from a JavaScript call, it renders a js.erb file called form_errors.js.erb and passes appropriate hash. I have also created another local variable called notice that is used as a temporary flash.
The form_errors.js.erb file
Since rails form helpers handle creation of forms the same way no matter what the object is, to reduce code duplication, using the same error handling file goes a long way.
The following is what I typically put into my form error handling js.erb file:
1 | // to hold the form element IDs |
2 | var errorIDs = new Array(); |
3 | |
4 | // to hold the error messages |
5 | var errorMessages = new Array(); |
Since Rails holds the messages and object ID’s in separate methods on the object it returns, it is easiest to store these in two separate arrays to call together later using their index.
The next part is crucial to finding and storing the DOM elements on the page that have validation errors. Using the way Rails form helpers place ID’s on the fields (type_object), you can store the exact DOM object into the array. This is of course if you haven’t broken Rails convention in naming your objects.
1 | // Loop through all the error objects to story as JS objects |
2 | <% item.errors.each do |error_object| %> |
3 | <% if params[:from_modal] %> |
4 | errorIDs.push("#<%= item.type.downcase %>_<%= error_object %>"); |
5 | <% else %> |
6 | errorIDs.push("#<%= item.type.downcase %>_<%= error_object %>"); |
7 | <% end %> |
8 | <% end %> |
Next you want to loop through all the error messages and store them in the array.
1 | // Loop through all the error messages to store for use |
2 | <% item.errors.full_messages.each do |msg| %> |
3 | errorMessages.push("<%= msg %>"); |
4 | <% end %> |
Now to handle these freshly created arrays and display the errors:
1 | // Attach the messages to the error form objects and show some visibles |
2 | $.each(errorIDs, function(index, value){ |
3 | $(errorIDs[index]).attr(“title”, errorMessages[index]).addClass(“error_field”); |
4 | }); |
This is a quick example of how to handle the form errors by giving the DOM element a title and adding a class to signify that it’s an error field. I like to use a jQuery plugin called QTip which you can place on the form fields. When the field is then selected, a tooltip pops up with the error message.
Other than that, styling up the form errors is all you have to do. Of course, there are many ways to validate a form before it is even sent to the server, but this is a great fall back method until you have that in place.