Thumb marko Marko Ćilimković Wednesday, December 9, 2015

Ah, the form is finally working. Now all that's left to do is to design it, style it and improve the user experience. Nothing too important, right? Well, maybe that would be the case in the 90s. UX is a big part of web applications development process that can significantly improve all aspects of your application. It will influence the users decision whether to come back and use your app or to decide not to use it and be irritated with the experience. Having unhappy users is bad, mkay? With Twitter, Facebook and other social networks users can spread the bad word really fast. To avoid it, let's look at how we can improve forms in our Rails web applications using inline validations and no plugins, gems, etc.

What are those?!

Inline validations are a type of form validations that are displayed to the user during the time of filling the form fields out. They can greatly lower the time a user submits his information, but also boost the experience and make users more satisfied. Imagine having a similar form.

Forms can hide a lot of validation from the user and if the fields aren't properly marked required it can have a negative effect when the user submits a form and sees a bunch of errors that he wasn't aware of.

The after submit validations are in a sense "rude" to the user because everything is thrown at him. The form wasn't trying to be polite and point out an error happened in the form, making the user happy he got such a minor response. Remember, feedback is good! Especially when timed right and when it's helpful.

With inline validations the user gains trust while filling out the form and doesn't feel alone, because he "talks" to someone and receives feedback.

Implementation from scratch

The process of turning your boring forms into beautiful talkative and user-friendly ones in Rails is fairly simple. You just need a good plan.

1. Everyone's favorite language: Building the javascript handle request

The best time for alerting a user of making an error would be when he clicks away from a field and focuses on another. There is a convenient trigger in javascript, called onfocus which is going to call the method that receives information whether a field is properly input through an AJAX request. I used CoffeeScript because it's way more awesome than javascript :) 

app/assets/javascripts/inline_validations.js.coffeejQuery ->
  $(".js_inline_validate .js_validate_me").on 'blur', ->
field = this
field_name = this.id.replace('event_', '')
field_value = $(field).val()
url = $(field).closest('form').data('validateUrl') + '?' + field_name + '=' + field_value
$.ajax url,
type: 'POST'
dataType: "json"
success: (data, textStatus, jqXHR) ->
field = $("." + 'js_event_' + data['field_name'])
field_group = field.closest('.form-group')
field.next('.help-block').remove()
if data['valid']
field_group.removeClass('has-error')
field_group.addClass('success-block')
else
field_group.addClass('has-error')
field_group.append("<span class='help-block'>" + data['message'] + "</span>")


2. Controller method as the brains

The method needs to be placed within the controller which is handling the newly initialized object. Its job is to receive the user input with the name of the field and check out whether it's valid or not. This is where Rails helps with its amazing ActiveRecord Validations. Lastly, it sends a JSON object as a response that holds all the info.

app/controllers/events_controller.rbclass EventsController < ApplicationController
# Basic RESTful resource methods omitted
# .
# .
def validate
    event = Event.new(validate_params)
    event.valid?
    event_field = validate_params.keys.first.try(:to_sym)
    validation_response = !event.errors.include?(event_field)
    respond_to do |format|
      format.json { render json: {field_name: event_field, valid: validation_response, message: event.errors[event_field]} }
    end
  end

  private
  def validate_params
    params.permit(:name, :email, :category_id, :content, :participants, :from, :to)
  end
end

3. Javascript hooks so that nothing goes to waste

This step is the glue between the first two steps. We basically have all the logic, but nothing is triggering the created methods. Javascript hooks are simple class names that trigger the javascript snippet. You can call them any way you like. Here at Bamboolab we have a convention of naming javascript hooks with a prefix "js_" which follows a descriptive name. That way we let the frontend developers know that the element they want to change triggers something, so they need to be extra careful. The view that holds the forms looks like this:

app/views/events/_form.html.erb<%= form_for @event, html: {class: 'js_inline_validate'}, data: {validate_url: validate_events_path} do |f| %>
<%= f.text_field :name, class: 'js_validate_me js_event_name' %>
<%= f.text_field :email, class: 'js_validate_me js_event_email' %>
<%= f.select :category_id, options_from_collection_for_select(Category.all, :id, :name), {prompt: 'Choose category'}, class: 'js_validate_me js_event_category_id' %>
<%= f.text_field :from, class: 'datepicker' %>
<%= f.text_field :to, class: 'datepicker js_validate_me js_event_to' %>
<%= f.text_field :participants, class: 'js_validate_me js_event_participants' %>
<%= f.text_area :content, rows: 15, class: 'js_validate_me js_event_content' %>
<%= f.submit %>
<% end %>

I omitted the div elements used for field positioning so that you can focus on the js hooks. Basically, they are added on all the fields that need to be validated. The form also has a hook, but it also needs a data attribute that is going to pass the URL (validate_events_path) of the controller method to the javascript snippet.

4. Routes, cause getting lost is bad

Of course, we need to create the new route:

config/routes.rbRails.application.routes.draw do
  root 'events#new'
  resources :events do
    collection { post :validate }
  end
end

Wrapping up

There are many ways of implementing inline validations. Sometimes you might just want to use some javascript plugin or a Ruby gem if you're a Rails developer. If so, I'd recommend client side validations, a Ruby gem created for this specific situation. But there are times when a plugin/gem just doesn't do what it's supposed to do and you cannot find a way around it. If you find yourself stuck like that, remember that everything can be implemented from scratch, where there won't be any confusing third-party codebase and where you'll have the ball in your playground :)



Cookies help us deliver our services. By using our services, you agree to our use of cookies.