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:
Thank you!
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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With