Type a keyword and hit enter to start searching. Press Esc to cancel.

Currently Reading

AngularJS Form Submission to Rails with Error Handling

Using an AngularJS based frontend, paired with a Rails backend, is a great stack.  AngularJS provides $resource, which is a mostly complete module to interface with the Rails backend and provide CRUD actions.  However, if you are using strong parameters with a require statement in Rails, then $resource can be a bit of a pain.

When generating forms with ActionView, you use form_for @model, this will generate input names with a name like name="model[attribute]".  Using this same naming scheme in AngularJS does not work, because the input names are not correctly interpreted as a JSON object. So, instead of using $resource, we can create our own form submission service.

NOTE: There is a dependency on jquery.serializeJSON, https://github.com/marioizquierdo/jquery.serializeJSON

angular.module("MyApp").service 'FormService', ($http) ->

  class Form

    submit: ($form) ->
      $http
        url: $form.attr('action')
        method: $form.attr('method')
        data: $form.serializeJSON()


    displayErrors: ($form, data) =>
      errors = @formatErrors(data)

      for input, messages of errors
        $input = $form.find("input[name=#{input}], input[name*=\"[#{input}]\"], select[name=#{input}], select[name*=\"[#{input}]\"], textarea[name=#{input}], textarea[name*=\"[#{input}]\"]")

        if $input.length > 0
          $group = $input.parent()
          $group.addClass('has-error')
          $group.append("<p class='help-block error-message'>#{messages.join("<br/>")}</p>")

      return


    clearErrors: ($form) =>
      $form.find('.has-error').removeClass('has-error')
      $form.find('p.error-message').remove()

      return


    formatErrors: (data) ->
      data.errors = {} if !data.errors?
      data.errors.base = [] if !data.errors.base?
      data.errors['base'].push(data.error) if data.error?

      return data.errors


  return new Form()

The submit() function in the above service is capable of serializing form input as a JSON object and submitting it to the form’s action using the form’s method by way of the AngularJS $http module.  This will pass back a promise that can be handled by the caller.  If the form returned an error, we can use the displayErrors() method to display the error messages on each input.

Below is an applied example using  FeedbackService.  This service is in charge of handling the submission of a feedback form on an application.

angular.module("MyApp").service 'FeedbackService', (FormService) ->

  class Feedback
    _form: $('#feedback-form')

    constructor: ->
      @_form.on 'submit', @submit

    submit: (event) =>
      event.preventDefault()

      FormService.clearErrors(@_form)
      result = FormService.submit(@_form)

      result.success =>
        alert('Your feedback has been submitted. Thank You.')

      result.error (data) =>
        FormService.displayErrors(@_form, data)


  return new Feedback()

As we can see the submit() function in FeedbackService will first clear any errors, then submit the form, and handle the submission promise by either displaying errors or giving a simple success alert message.

While the form submission works great for all Rails forms, the error displaying is not perfect.  Nested resources may not be correctly identified and have the wrong error displayed

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *