Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grails JQuery Ajax Form Validation

I have a modal dialog where I load a form from a gsp and I am struggling to find the right solution for an ajax based form with JSON validation incl. success message like a flash scope message:

$('#calendar_form').live('click', function () {
        $.modal({
            ajax: './form'
            , title: '${message(code:'calendar.main.addAppointment')}'
            , overlayClose: true
        });
    });

In that form I have the following JS:

$(document).ready(function() {
    $('#form1').submit(function() {

        $.ajax({
            type: 'POST',
            url: '${createLink(action:'post')}',
            data: $("#form1").serialize(),
            success: function(result) {
                alert(result);
            }
        });
    });
});

I am returning a JSON response if its successful and when an error occurs and I need Ajax otherwise my modal dialog is disapearing due to the new request.

So here are my questions:

  • How do I react on failures which happen during my validation in my controller?
  • Which error http codes do I need to use for validation errors in my controller?
  • How do I update the specific fields with error messages in my form?
  • How do I update the flash part of my page to render the success msg?
  • How is it possible to update other parts of the page after success?

Thank you!

like image 984
Gambo Avatar asked Oct 10 '11 20:10

Gambo


2 Answers

There are a lot of different ways to accomplish this and probably 1 or more plugins to get you going. However, I'll show you how I generally deal with this. I have an object that looks like this...

class AjaxPostResponse {
  boolean success
  String message
  String html
  def domainObject
  def errors = [:] 
}

This is the object I render as JSON. So if there are validation errors, success becomes false and I add all the errors to the errors map. I do this in a service and that method looks like this:

def preparePostResponse(domainInstance) {
    def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
    def postResponse = new AjaxPostResponse(domainObject: domainInstance)
    if (domainInstance.hasErrors()) {
      g.eachError(bean: domainInstance) {
        postResponse.errors."${it.field}" = g.message(error: it)
      }
      postResponse.success = false
      postResponse.message = "There was an error"
    } else {
      postResponse.success = true
      postResponse.message = "Success"
    }
    return postResponse
}

So my controller looks something like

def save = {
  def someObjInstance = new SomeObj(params)
  someObjInstance.save(flush:true)
  render myService.preparePostResponse(someObjInstance) as JSON
}

In my client side code I do something like this (using the jQuery form plugin, but this would work with a generic $.ajax / $.post / $.get method as well...

$(formElement).ajaxSubmit({
    dataType: 'json',
    success: function(jsonData) {
      if (jsonData.success) {
        // do good stuff
      } else {
        // bad stuff happened
        showErrors(jsonData.errors);
      }
    }
});

And my showErrors function

function showErrors(errors, element) {
    var errorList = $("<ul>");
    for (field in errors) {
        errorList.append("<li>" + errors[field] + "</li>")
        $('input[name=' + field + ']').addClass('error');
    }
    if (!element) {
        $(".errors").html("").append(errorList).show(500);
    } else {
        $(element).html("").append(errorList).show(500);
    }
}

Hope that helps.

like image 113
Gregg Avatar answered Nov 06 '22 13:11

Gregg


The accepted answer is good and I'm all for a service centric approach. However with Ajax controllers I find sometimes the requirements are very simple and error handling and persistence can be done right in the controller. A nice oneliner to add your nice i18n errors to a collection is:

if (!yourdomain.validate())
 {
     errors.addAll(yourdomain.errors.allErrors.collect {message(error: it)})
 } else {
     yourdomain.save(); //etc . . .
}

Then return JSON as follows

render(contentType: "text/json") {
     if (errors)
     {
         success = 'false'
         errorList = errors
     } else {
         success = 'true'
         //otherstuff
     }
}

And your JavaScript (Grails 2 has a documentation bug, so use 'data' not 'e' if using JQuery as is the default.

Call your Ajax

<g:formRemote /*or remoteLink */ ...your URL, etc... onSuccess="doResponse(data)">
<div class="alert" style="display: none" id="error"></div>

And process the response

<g:javascript>
        function doResponse(data) {
            if (data.success == 'true') {
                //success stuff
            } else {
                var errorList = $('<ul class="errors">');
                for (var i = 0; i < data.errorList.length; i++) {
                    errorList.append('<li>' + data.errorList[i] + "</li>");
                }

                $('#error').html(errorList);
                $('#error').show();
            }

        }
   </g:javascript>
like image 20
Peter Avatar answered Nov 06 '22 13:11

Peter