diff --git a/grails-app/controllers/org/grails/jquery/validation/ui/JQueryRemoteValidatorController.groovy b/grails-app/controllers/org/grails/jquery/validation/ui/JQueryRemoteValidatorController.groovy
index d325a48..d6678ad 100644
--- a/grails-app/controllers/org/grails/jquery/validation/ui/JQueryRemoteValidatorController.groovy
+++ b/grails-app/controllers/org/grails/jquery/validation/ui/JQueryRemoteValidatorController.groovy
@@ -15,6 +15,7 @@
package org.grails.jquery.validation.ui
import org.codehaus.groovy.grails.validation.ConstrainedPropertyBuilder
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory
import org.springframework.validation.BeanPropertyBindingResult
/**
@@ -34,6 +35,9 @@ class JQueryRemoteValidatorController {
def validatableInstance
if (!params.id || params.id.equals("0")) {
validatableInstance = validatableClass.newInstance()
+ // Wire in spring dependencies...
+ applicationContext.autowireCapableBeanFactory?.autowireBeanProperties(
+ validatableInstance, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false)
} else {
validatableInstance = validatableClass.get(params.id.toLong())
}
@@ -51,17 +55,13 @@ class JQueryRemoteValidatorController {
}
constrainedProperty.validate(validatableInstance, propertyValue, errors)
- if(validatableInstance.isAttached()) validatableInstance.discard()
+ if (grailsApplication.isDomainClass(validatableInstance.getClass()) && validatableInstance.isAttached()) {
+ validatableInstance.discard()
+ }
def fieldError = errors.getFieldError(params.property)
// println "fieldError = ${fieldError}, code = ${fieldError?.code}, params.constraint = ${params.constraint}"
response.setContentType("text/json;charset=UTF-8")
- if (fieldError && fieldError.code.indexOf(params.constraint) > -1) {
- // if constraint is known then render false (use default message),
- // otherwise render custom message.
- render params.constraint ? "false" : """{"message":"${message(error: errors.getFieldError(params.property))}"}"""
- } else {
- render "true"
- }
+ render fieldError ? """{"message":"${message(error: fieldError)}"}""" : "true"
}
}
diff --git a/grails-app/services/org/grails/jquery/validation/ui/JqueryValidationService.groovy b/grails-app/services/org/grails/jquery/validation/ui/JqueryValidationService.groovy
index d4231a1..145529a 100644
--- a/grails-app/services/org/grails/jquery/validation/ui/JqueryValidationService.groovy
+++ b/grails-app/services/org/grails/jquery/validation/ui/JqueryValidationService.groovy
@@ -18,7 +18,9 @@ import org.codehaus.groovy.grails.validation.ConstrainedPropertyBuilder
import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
import org.codehaus.groovy.grails.web.pages.FastStringWriter
import grails.util.GrailsNameUtils
+import grails.validation.ValidationErrors
import org.springframework.web.context.request.RequestContextHolder
+import net.zorched.grails.plugins.validation.CustomConstraintFactory
/**
*
@@ -53,7 +55,27 @@ class JqueryValidationService {
nullable: "default.null.message",
validator: "default.invalid.validator.message",
unique: "default.not.unique.message"
- ]
+ ]
+
+ static final GRAILS_CONSTRAINT_FAILURE_CODES_MAP = [
+ blank:'blank',
+ creditCard:'creditCard.invalid',
+ email:'email.invalid',
+ inList:'not.inList',
+ matches:'matches.invalid',
+ max:'max.exceeded',
+ maxSize:'maxSize.exceeded',
+ min:'min.notmet',
+ minSize:'minSize.notmet',
+ notEqual:'notEqual',
+ nullable:'nullable',
+ range:null,
+ size:null,
+ unique:'unique',
+ url:'url.invalid',
+ validator:'validator.invalid'
+ ]
+
static transactional = false
def grailsApplication
def messageSource
@@ -158,12 +180,13 @@ class JqueryValidationService {
return constraintsMap
}
- private List getConstraintNames(def constrainedProperty) {
- def constraintNames = constrainedProperty.appliedConstraints.collect { return it.name }
- if (constraintNames.contains("blank") && constraintNames.contains("nullable")) {
- constraintNames.remove("nullable") // blank constraint take precedence
+ private List getConstraints(def constrainedProperty) {
+ def constraints = []
+ constraints.addAll(constrainedProperty.appliedConstraints)
+ if (constraints.find{it.name == "blank"} && constraints.find {it.name == "nullable"}) {
+ constraints.removeAll {it.name == "nullable"} // blank constraint take precedence
}
- return constraintNames
+ return constraints
}
private String createRemoteJavaScriptConstraints(String contextPath, String constraintName, String validatableClassName, String propertyName) {
@@ -212,25 +235,65 @@ class JqueryValidationService {
return message.encodeAsJavaScript()
}
- private String getMessage(Class validatableClass, String propertyName, def args, String constraintName, Locale locale) {
- def code = "${validatableClass.name}.${propertyName}.${constraintName}"
- def defaultMessage = "Error message for ${code} undefined."
- def message = messageSource.getMessage(code, args == null ? null : args.toArray(), null, locale)
-
- ERROR_CODE_SUFFIXES.each { errorSuffix ->
- message = message?:messageSource.getMessage("${code}.${errorSuffix}", args == null ? null : args.toArray(), null, locale)
- }
- if (!message) {
- code = "${GrailsNameUtils.getPropertyName(validatableClass)}.${propertyName}.${constraintName}"
+ private String getMessage(def constraint, Class validatableClass, def args, Locale locale) {
+ def argArray = args == null ? null : args.toArray()
+ String defaultMessageCode
+ String message
+ // The preferred way to do things is to create an object to validate and pretend that a validation error occurred
+ // so that we simulate as closely as possible all of the error codes that grails generates on the server side.
+ // Another approach would be to copy the code from AbstractConstraint that does all the work of generating all of
+ // the message code possibilities and sticks them into the Errors object here. That would allow us to eliminate the
+ // else case below. It would come at the cost of having to make sure it was kept in sync with the grails source from
+ // which is was copied. It would be nice if grails would refactor that bit of code into a utility class that we
+ // could call...
+ if (!grailsApplication.config.jqueryValidationUi.useLegacyMessageCodes && constraint.respondsTo('rejectValue')) {
+ String failureCode
+ // Handle custom constraints correctly by getting the default message code, default message and the
+ // failure code to use. It would be nice if the standard grails constraints also worked this way, but
+ // unfortunately we have to just hard code in the values for those constraints based on the grails
+ // documentation...
+ if (constraint instanceof CustomConstraintFactory.CustomConstraintClass) {
+ defaultMessageCode = constraint.constraint.getDefaultMessageCode()
+ failureCode = constraint.constraint.getFailureCode()
+ } else {
+ failureCode = GRAILS_CONSTRAINT_FAILURE_CODES_MAP[constraint.name]
+ }
+ // Fake a call to reject the constraint which should result in our validationErrors object having a single
+ // FieldError in it which will contain a list of the standard grails message codes for validation failure
+ // in the correct search order...
+ def targetObject = validatableClass.newInstance()
+ def validationErrors = new ValidationErrors(targetObject, constraint.propertyName)
+ // Casts to string needed here to avoid ambiguous method overloading exceptions since sometimes the values of
+ // defaultMessageCode and failureCode are null...
+ constraint.rejectValue(targetObject, validationErrors, (String)defaultMessageCode, (String)failureCode, argArray)
+ message = validationErrors.fieldErrors[0].codes.findResult {messageSource.getMessage(it, argArray, null, locale)}
+ } else {
+ // This is not the common case, but could happen if someone implemented Constraint without subclassing
+ // AbstractConstraint. This is a rather inaccurate attempt at trying to find a message from a series of message code
+ // possibilities. It is inaccurate because the name of the constraint often can't be generated by tacking on '.error'
+ // or '.invalid' to the end of ${classname}.${constraint.propertyName}.${constraint.name}. Also, for custom constraints, the user
+ // can define whatever message code they want, so we're not respecting that at all here. Finally, for custom
+ // constraints, the default message code and default message can be defined, but there is no attempt to even try to
+ // find a default value for a custom constraint here...
+ def code = "${validatableClass.name}.${constraint.propertyName}.${constraint.name}"
message = messageSource.getMessage(code, args == null ? null : args.toArray(), null, locale)
- }
+
+ ERROR_CODE_SUFFIXES.each { errorSuffix ->
+ message = message?:messageSource.getMessage("${code}.${errorSuffix}", argArray, null, locale)
+ }
+ if (!message) {
+ code = "${GrailsNameUtils.getPropertyName(validatableClass)}.${constraint.propertyName}.${constraint.name}"
+ message = messageSource.getMessage(code, argArray, null, locale)
+ }
- ERROR_CODE_SUFFIXES.each { errorSuffix ->
- message = message?:messageSource.getMessage("${code}.${errorSuffix}", args == null ? null : args.toArray(), null, locale)
+ ERROR_CODE_SUFFIXES.each { errorSuffix ->
+ message = message?:messageSource.getMessage("${code}.${errorSuffix}", argArray, null, locale)
+ }
}
if (!message) {
- code = DEFAULT_ERROR_MESSAGE_CODES_MAP[constraintName]
- message = messageSource.getMessage(code, args == null ? null : args.toArray(), defaultMessage, locale)
+ String defaultMessage = "Property [{0}] of class [{1}] with value [{2}] is not valid"
+ defaultMessageCode = defaultMessageCode ?: DEFAULT_ERROR_MESSAGE_CODES_MAP[constraint.name]
+ message = messageSource.getMessage(defaultMessageCode, argArray, defaultMessage, locale)
}
return message.encodeAsJavaScript()
}
@@ -243,7 +306,7 @@ class JqueryValidationService {
javaScriptConstraints << "{ "
constraintsMap = getConstraintsMap(constrainedProperty.propertyType)
- def constraintNames = getConstraintNames(constrainedProperty)
+ def constraints = getConstraints(constrainedProperty)
switch (constrainedProperty.propertyType) {
case Date:
@@ -264,17 +327,17 @@ class JqueryValidationService {
if (javaScriptConstraintCode) {
javaScriptConstraints << javaScriptConstraintCode
- if (constraintNames.size() > 0) {
+ if (constraints.size() > 0) {
javaScriptConstraints << ", "
} else {
javaScriptConstraints << " "
}
}
- constraintNames.eachWithIndex { constraintName, i ->
- javaScriptConstraint = constraintsMap[constraintName]
+ constraints.eachWithIndex { constraint, i ->
+ javaScriptConstraint = constraintsMap[constraint.name]
javaScriptConstraintCode = null
if (javaScriptConstraint) {
- switch (constraintName) {
+ switch (constraint.name) {
case "nullable":
if (!constrainedProperty.isNullable()) {
javaScriptConstraintCode = "${javaScriptConstraint}: true"
@@ -347,21 +410,35 @@ class JqueryValidationService {
break
case "unique":
case "validator":
- javaScriptConstraintCode = createRemoteJavaScriptConstraints(RequestContextHolder.requestAttributes.contextPath, constraintName, constrainedProperty.owningClass.name, constrainedProperty.propertyName)
+ javaScriptConstraintCode = createRemoteJavaScriptConstraints(RequestContextHolder.requestAttributes.contextPath, constraint.name, constrainedProperty.owningClass.name, constrainedProperty.propertyName)
+ break
+ default:
+ // custom constraint...
+ def customConstraintsMap = grailsApplication.config.jqueryValidationUi.CustomConstraintsMap
+ if (customConstraintsMap && customConstraintsMap[constraint.name]) {
+ javaScriptConstraintCode = "${javaScriptConstraint}: ${customConstraintsMap[constraint.name]}"
+ } else {
+ log.error "Failed to generate javascript validation rule for constraint '${constraint.name}' " +
+ "with javascript constraint '${javaScriptConstraint}': missing custom constraints map " +
+ "entry. Ignoring this constraint and moving on."
+ }
break
}
} else {
+ // Old way of generating the custom constraint javascript rule is to assume the javascript constraint validation method is
+ // the same name as the grails constraint. The preferred way to do things now is to create a map entry in the appropriate
+ // map (string, number, date, etc.) for the custom constraint as is done for the built-in grails constraints...
def customConstraintsMap = grailsApplication.config.jqueryValidationUi.CustomConstraintsMap
- if (customConstraintsMap && customConstraintsMap[constraintName]) {
- javaScriptConstraintCode = "$constraintName: ${customConstraintsMap[constraintName]}"
+ if (customConstraintsMap && customConstraintsMap[constraint.name]) {
+ javaScriptConstraintCode = "${constraint.name}: ${customConstraintsMap[constraint.name]}"
} else {
- log.info "${constraintName} constraint not found even in the CustomConstraintsMap, use custom constraint and remote validation"
+ log.info "${constraint.name} constraint not found even in the CustomConstraintsMap, use custom constraint and remote validation"
javaScriptConstraintCode = createRemoteJavaScriptConstraints(RequestContextHolder.requestAttributes.contextPath, constraintName, constrainedProperty.owningClass.name, constrainedProperty.propertyName)
}
}
if (javaScriptConstraintCode) {
javaScriptConstraints << javaScriptConstraintCode
- if (i < constraintNames.size() - 1) {
+ if (i < constraints.size() - 1) {
javaScriptConstraints << ", "
} else {
javaScriptConstraints << " "
@@ -382,13 +459,13 @@ private String _createJavaScriptMessages(def constrainedProperty, Locale locale,
def args = []
FastStringWriter javaScriptMessages = new FastStringWriter(VALIDATION_MESSAGE_LENGTH)
String javaScriptConstraint
-def constraintNames
+def constraints
String javaScriptMessageCode
def constraintsMap = getConstraintsMap(constrainedProperty.propertyType)
javaScriptMessages << "{ "
-constraintNames = getConstraintNames(constrainedProperty)
+constraints = getConstraints(constrainedProperty)
javaScriptMessageCode = null
switch (constrainedProperty.propertyType) {
case Date:
@@ -412,27 +489,27 @@ case BigDecimal:
if (javaScriptMessageCode) {
javaScriptMessages << javaScriptMessageCode
-if (constraintNames.size() > 0) {
+if (constraints.size() > 0) {
javaScriptMessages << ", "
} else {
javaScriptMessages << " "
}
}
-constraintNames.eachWithIndex { constraintName, i ->
-javaScriptConstraint = constraintsMap[constraintName]
+constraints.eachWithIndex { constraint, i ->
+javaScriptConstraint = constraintsMap[constraint.name]
javaScriptMessageCode = null
args.clear()
args = [constrainedProperty.propertyName, constrainedProperty.owningClass]
if (javaScriptConstraint) {
- switch (constraintName) {
+ switch (constraint.name) {
case "nullable":
if (!constrainedProperty.isNullable()) {
- javaScriptMessageCode = "${javaScriptConstraint}: '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'"
+ javaScriptMessageCode = "${javaScriptConstraint}: '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'"
}
case "blank":
if (!constrainedProperty.isBlank()) {
- javaScriptMessageCode = "${javaScriptConstraint}: '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'"
+ javaScriptMessageCode = "${javaScriptConstraint}: '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'"
}
break
case "creditCard":
@@ -440,7 +517,7 @@ if (javaScriptConstraint) {
case "url":
if (constrainedProperty.isCreditCard() || constrainedProperty.isEmail() || constrainedProperty.isUrl()) {
args << VALUE_PLACEHOLDER
- javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'; }".replace(
+ javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'; }".replace(
VALUE_PLACEHOLDER, "' + \$('#${constrainedProperty.propertyName}').val() + '");
}
break
@@ -452,39 +529,51 @@ if (javaScriptConstraint) {
case "minSize":
case "notEqual":
args << VALUE_PLACEHOLDER
- args << constrainedProperty."${constraintName}"
- javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'; }".replace(
+ args << constrainedProperty."${constraint.name}"
+ javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'; }".replace(
VALUE_PLACEHOLDER, "' + \$('#${constrainedProperty.propertyName}').val() + '");
break
case "range":
case "size":
args << VALUE_PLACEHOLDER
- def range = constrainedProperty."${constraintName}"
+ def range = constrainedProperty."${constraint.name}"
args << range.from
args << range.to
- javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'; }".replace(
+ javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'; }".replace(
VALUE_PLACEHOLDER, "' + \$('#${constrainedProperty.propertyName}').val() + '");
break
case "unique":
case "validator":
args << VALUE_PLACEHOLDER
- javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'; }".replace(
+ javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'; }".replace(
VALUE_PLACEHOLDER, "' + \$('#${constrainedProperty.propertyName}').val() + '");
break
+ default:
+ // custom constraint...
+ def customConstraintsMap = grailsApplication.config.jqueryValidationUi.CustomConstraintsMap
+ if (customConstraintsMap && customConstraintsMap[constraint.name]) {
+ args << VALUE_PLACEHOLDER
+ javaScriptMessageCode = "${javaScriptConstraint}: function() { return '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'; }".replace(
+ VALUE_PLACEHOLDER, "' + \$('#${constrainedProperty.propertyName}').val() + '");
+ }
+ break
}
} else {
+ // Old way of generating the custom constraint javascript message is to assume the javascript constraint validation method is
+ // the same name as the grails constraint. The preferred way to do things now is to create a map entry in the appropriate
+ // map (string, number, date, etc.) for the custom constraint as is done for the built-in grails constraints...
def customConstraintsMap = grailsApplication.config.jqueryValidationUi.CustomConstraintsMap
- if (customConstraintsMap && customConstraintsMap[constraintName]) {
+ if (customConstraintsMap && customConstraintsMap[constraint.name]) {
args << VALUE_PLACEHOLDER
- javaScriptMessageCode = "${constraintName}: function() { return '${getMessage(constrainedProperty.owningClass, constrainedProperty.propertyName, args, constraintName, locale)}'; }".replace(
+ javaScriptMessageCode = "${constraint.name}: function() { return '${getMessage(constraint, constrainedProperty.owningClass, args, locale)}'; }".replace(
VALUE_PLACEHOLDER, "' + \$('#${constrainedProperty.propertyName}').val() + '");
} // else remote validation, using remote message.
}
if (javaScriptMessageCode) {
javaScriptMessages << javaScriptMessageCode
- if (i < constraintNames.size() - 1) {
+ if (i < constraints.size() - 1) {
javaScriptMessages << ", "
} else {
javaScriptMessages << " "
diff --git a/src/docs/guide/usage/features.gdoc b/src/docs/guide/usage/features.gdoc
index a08e451..45b0e87 100644
--- a/src/docs/guide/usage/features.gdoc
+++ b/src/docs/guide/usage/features.gdoc
@@ -29,8 +29,16 @@ h3. Extensibility
Together with the [custom constraints|http://github.com/geofflane/grails-constraints] plugin, the plugin is fully extensible with your own custom validation logic.
-The plugin come with 2 custom constraints, phone and phoneUS (International and US phone number validation) which enabled by the following configuration:
+The plugin comes with 2 custom constraints, phone and phoneUS (International and US phone number validation) which are enabled by the following configuration:
{code}
+StringConstraintsMap = [
+ blank:'required', // inverse: blank=false, required=true
+ creditCard:'creditcard',
+ ...,
+ phone:'phone',
+ phoneUS:'phoneUS',
+]
+
CustomConstraintsMap = [
phone:'true',
phoneUS:'true'
@@ -39,13 +47,25 @@ CustomConstraintsMap = [
If you implement new custom constraints (both server-side and Javascript), you can enable it by adding the constraints to the @CustomConstraintsMap@, for example:
{code}
+StringConstraintsMap = [
+ blank:'required', // inverse: blank=false, required=true
+ creditCard:'creditcard',
+ ...,
+ phone:'phone',
+ phoneUS:'phoneUS',
+ yourCustomConstraint:'jqueryValidationPluginCustomConstraint'
+]
+
CustomConstraintsMap = [
phone:'true',
phoneUS:'true',
- yourCustomConstraint:'Javascript Code'
+ yourCustomConstraint:'jqueryValidationPluginCustomConstraintParamGeneratorJavascript'
]
{code}
-The @'Javascript Code'@ is Javascript code specific to your custom constraints. This will be rendered by @@ tag. Please refer to
+The @'jqueryValidationPluginCustomConstraint'@ is the name of the custom jquery validation plugin method to use for client-side validation.
+The @'jqueryValidationPluginCustomConstraintParamGeneratorJavascript'@ is javascript that will be used to supply the parameter to
+the jqueryValidationPluginCustomConstraint.
+These values will be used by the @@ tag. Please refer to the
source code of the server-side implementation of phone constraint
[here|http://github.com/limcheekin/jquery-validation-ui/blob/master/test/unit/org/grails/jquery/validation/ui/PhoneConstraintTests.groovy]
and the client-side implementation
@@ -54,7 +74,7 @@ and the client-side implementation
h3. Internationalization Support
All client-side validation messages retrieve from messages.properties. So, both client-side and server-side validation using the same message bundle.
-The plugin retrieve the validation message with following codes from top to bottom:
+Prior to version 1.4.5, the plugin retrieved the validation message by trying the following codes in this order:
{code}
classFullName.property.constraint
@@ -65,7 +85,36 @@ className.property.constraint.error
className.property.constraint.invalid
{code}
-If it is not found, it will use the default message.
+As of version 1.4.5, the plugin now searches for an error message using the same error codes used by the grails server-side validation process. It also
+respects the @defaultMessageCode@, @defaultMessage@, and @failureCode@ settings on custom constraints when searching for an error message. This means that
+it should now be much easier to know which message codes you should use in your message bundles since the server-side and client-side now use the same
+codes and search order. The one exception to this is if a grails constraint returns different message codes depending on the kind
+of validation failure. For example, the grails size constraint returns @className.propertyName.size.toosmall@ or @className.propertyName.size.toobig@
+depending on whether the field value was too large or too small to satisfy the constraint. Since the plugin is simply generating the validation code,
+but doesn't yet know what the value will be, it has no way of knowing which validation message it should insert into the javascript. It may be possible
+to generate javascript that would handle both cases on the client in the future. However, for now the workaround is to use the message code
+@className.propertyName.size.error@ (or other standard grails message code for the size constraint that doesn't have 'toobig' or 'toosmall' in it) with a
+message that will work for when the value is too large or too small. Another option for the size constraint is to use the @minSize@ and @maxSize@ constraints
+instead of the @size@ constraint.
+
+Most pre-1.4.5 applications will be mostly compatible with the new behavior as most of the error codes used by the old behavior are also used by the standard
+grails validation process. The one difference is that in some cases the error codes with the suffix of '.invalid' or the error codes with no '.error' or
+'.invalid' suffix that were used by the plugin prior to 1.4.5 are not used by the standard grails validation process. If you have these message codes in your
+message bundles, you will need to update them to use a standard grails message code instead. For example, the message code @className.property.blank.invalid@
+is not a standard grails message code, but it was searched for by this plugin prior to version 1.4.5. You would need to change your message bundles to use a
+standard grails message code instead. For example, the codes @className.property.blank.error@ or @className.property.blank@ are standard grails message codes for
+the blank constraint and would be valid replacements for the @className.property.blank.invalid message@ code that worked prior to grails 1.4.5. For backwards
+compatibility, you can add the following line:
+
+{code}
+useLegacyMessageCodes=false
+{code}
+
+to the @jqueryValidationUi@ section of your @Config.groovy@ to use the pre-1.4.5 message codes. However, please be aware that the pre-1.4.5 behavior is
+deprecated, so this option is only made available to allow users to upgrade the plugin and have time to convert their message codes to standard grails
+codes. The useLegacyMessageCodes option will be removed in a future release.
+
+If a message cannot be found from the message codes, the default message will be used.
h3. Type Validation Support
The plugin supports type validation for @Date@, @Long@, @Integer@, @Short@, @BigInteger@, @Float@, @Double@, and @BigDecimal@ and retrieves the corresponding
diff --git a/test/unit/org/grails/jquery/validation/ui/JqueryValidationServiceSpec.groovy b/test/unit/org/grails/jquery/validation/ui/JqueryValidationServiceSpec.groovy
index a492375..4410146 100644
--- a/test/unit/org/grails/jquery/validation/ui/JqueryValidationServiceSpec.groovy
+++ b/test/unit/org/grails/jquery/validation/ui/JqueryValidationServiceSpec.groovy
@@ -2,6 +2,7 @@ package org.grails.jquery.validation.ui
import spock.lang.*
import grails.test.mixin.*
+import org.codehaus.groovy.grails.validation.Constraint
import org.springframework.context.MessageSource
@TestFor(JqueryValidationService)
@@ -16,13 +17,16 @@ public class JqueryValidationServiceSpec extends Specification {
def "Escaped messages"() {
given:
MessageSource messageSource = Mock()
+ Constraint constraint = Mock()
service.messageSource = messageSource
when:
- def message = service.getMessage(this.class, "prop", null, "max", null)
+ def message = service.getMessage(constraint, this.class, null, null)
then:
1 * messageSource.getMessage("${this.class.name}.prop.max", null, null, null) >> "my 'message'"
+ constraint.name >> 'max'
+ constraint.propertyName >> 'prop'
0 * _._
message=="my \\'message\\'"
}