看到Grails的validation标签居然是没实现,一下子大受打击!很郁闷,但是没办法,偶们还是自己来实现一下这个重要的功能把.
Grails的tag其实一般都在\grails-app\taglib下面,可以自行修改源程序来修改tag的表现.validation标签就在ValidationTagLib.groovy这个文件里面.只要修改一下代码就可以了,由于实现过程比较繁杂,直接贴出源代码,需要注意的是:
第一个地方:” import org.geszjava.grails.utils.ChineseProperty as CP;”这条语句,偶是自己写一个类来进行属性名和表单说明的转换,这一步算是偷懒了.有好处也有坏处,好处是不用写那么多Local信息了,直接拿default的来就可以了.坏处是Locale的处理上有问题.当然我现在的项目只支持中文.:),以后有时间可以考虑本地化问题.BTW:grails自己的本地化支持目前好像还没有?不太清楚.呵呵.
第二个地方:”size”这种Type用的是”minLength,maxLength”,而不是原先的intRange,具体怎么回事用脑袋想想也知道:两者在javascript上的实现天差地远.
第三个地方:splitType(),由于有了”minLength,maxLength”这种类型,那么整合的时候当然不能直接写成validationMinLength,maxLength()这样的东东,可以说是个比较严重的错误,所以这里顺便更改了一下
第四个地方: 看看这段代码
原先的代码太粗略,minlength和maxlength不能工作,所以写个比较麻烦的代码,其实修改一下那个MAP就可以了,不过那样修改的东西太多了,不合算.
Grails的tag其实一般都在\grails-app\taglib下面,可以自行修改源程序来修改tag的表现.validation标签就在ValidationTagLib.groovy这个文件里面.只要修改一下代码就可以了,由于实现过程比较繁杂,直接贴出源代码,需要注意的是:
第一个地方:” import org.geszjava.grails.utils.ChineseProperty as CP;”这条语句,偶是自己写一个类来进行属性名和表单说明的转换,这一步算是偷懒了.有好处也有坏处,好处是不用写那么多Local信息了,直接拿default的来就可以了.坏处是Locale的处理上有问题.当然我现在的项目只支持中文.:),以后有时间可以考虑本地化问题.BTW:grails自己的本地化支持目前好像还没有?不太清楚.呵呵.
第二个地方:”size”这种Type用的是”minLength,maxLength”,而不是原先的intRange,具体怎么回事用脑袋想想也知道:两者在javascript上的实现天差地远.
第三个地方:splitType(),由于有了”minLength,maxLength”这种类型,那么整合的时候当然不能直接写成validationMinLength,maxLength()这样的东东,可以说是个比较严重的错误,所以这里顺便更改了一下
第四个地方: 看看这段代码
switch(vt) {
case 'mask':
out << "function ${form}_mask() {";
break;
case 'intRange':
out << "function ${form}_intRange() {";
break;
case 'floatRange':
out << "function ${form}_floatRange() {";
break;
case 'maxLength':
out << "function ${form}_maxlength() {";
break;
case 'minLength':
out << "function ${form}_minlength() {";
break;
case 'email':
out << "function ${form}_email() {";
break;
case 'creditCard':
out << "function ${form}_creditCard() {";
break;
case 'required':
out << "function ${form}_required() {";
break;
default:
out << "function ${form}_${vt}() {";
break;
}
原先的代码太粗略,minlength和maxlength不能工作,所以写个比较麻烦的代码,其实修改一下那个MAP就可以了,不过那样修改的东西太多了,不合算.
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT c;pWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.text.MessageFormat
import org.springframework.validation.Errors;
import org.springframework.context.NoSuchMessageException;
import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU;
import org.geszjava.grails.utils.ChineseProperty as CP;
/**
* A tag lib that provides tags to handle validation and errors
*
* @author Graeme Rocher
* @since 17-Jan-2006
*/
class ValidationTagLib {
/**
* Checks if the request has errors either for a field or global errors
*/
def hasErrors = { attrs, body ->
def model = attrs['model']
def checkList = []
if(model) {
checkList = model.findAll { k,v ->
grailsApplication.isGrailsDomainClass(v.class)
}
}
if(attrs['bean']) {
checkList << attrs['bean']
}
else {
if(request.attributeNames) {
request.attributeNames.each { ra ->
if(ra) {
if(ra instanceof Errors)
checkList << ra
else if(grailsApplication.isGrailsDomainClass(ra.class))
checkList << ra
}
}
}
}
for(i in checkList) {
def errors = null
if(grailsApplication.isGrailsDomainClass(i.class)) {
if(i.hasErrors())
errors = i.errors
}
else if(i instanceof Errors) {
errors = i
}
if(errors) {
if(attrs['field']) {
if(errors.hasFieldErrors(attrs['field'])) {
body()
}
}
else {
body()
}
}
}
}
/**
* Loops through each error for either field or global errors
*/
def eachError = { attrs, body ->
def model = attrs['model']
def errorList = []
if(model) {
errorList = model.findAll { k,v ->
grailsApplication.isGrailsDomainClass(v.class)
}
}
if(attrs['bean']) {
errorList << attrs['bean']
}
else {
request.attributeNames.each {
def ra = request[it]
if(ra) {
if(ra instanceof Errors)
errorList << ra
else if(grailsApplication.isGrailsDomainClass(ra.class))
errorList << ra
}
}
}
for(i in errorList) {
def errors = null
if(grailsApplication.isGrailsDomainClass(i.class)) {
if(i.hasErrors())
errors = i.errors
}
else if(i instanceof Errors) {
errors = i
}
if(errors && errors.hasErrors()) {
if(attrs['field']) {
if(errors.hasFieldErrors(attrs['field'])) {
errors.getFieldErrors( attrs["field"] ).each {
body(it)
}
}
}
else {
errors.allErrors.each {
body( it )
}
}
}
}
}
/**
* Loops through each error and renders it using one of the supported mechanisms (defaults to "list" if unsupported)
*/
def renderErrors = { attrs, body ->
def renderAs = attrs.remove('as')
if(!renderAs) renderAs = 'list'
if(renderAs == 'list') {
out << "<ul>"
eachError(attrs, {
out << "<li>"
message(error:it)
out << "</li>"
}
)
out << "</ul>"
}
}
/**
* Resolves a message code for a given error or code from the resource bundle
*/
def message = { attrs ->
def messageSource = grailsAttributes
.getApplicationContext()
.getBean("messageSource")
def locale = RCU.getLocale(request)
if(attrs['error']) {
def error = attrs['error']
def defaultMessage = ( attrs['default'] ? attrs['default'] : error.defaultMessage )
def message = messageSource.getMessage( error.code,
error.arguments,
defaultMessage,
locale )
if(message) {
out << message
}
else {
out << error.code
}
}
if(attrs['code']) {
def code = attrs['code']
def defaultMessage = ( attrs['default'] ? attrs['default'] : code )
def message = messageSource.getMessage( code,
null,
defaultMessage,
locale )
if(message) {
out << message
}
else {
out << code
}
}
}
// Maps out how Grails contraints map to Apache commons validators
static CONSTRAINT_TYPE_MAP = [ email : 'email',
creditCard : 'creditCard',
match : 'mask',
blank: 'required',
nullable: 'required',
maxSize: 'maxLength',
minSize: 'minLength',
range: 'intRange',
size: 'maxLength,minLength',
length: 'maxLength,minLength' ]
/**
* Validates a form using Apache commons validator javascript against constraints defined in a Grails
* domain class
*
* TODO: This tag is a work in progress
修改
*/
def validate = { attrs, body ->
def form = attrs["form"]
def againstClass = attrs["against"]
if(!form)
throwTagError("Tag [validate] is missing required attribute [form]")
if(!againstClass) {
againstClass = form.substring(0,1).toUpperCase() + form.substring(1)
}
def app = grailsAttributes.getGrailsApplication()
def dc = app.getGrailsDomainClass(againstClass)
if(!dc)
throwTagError("Tag [validate] could not find a domain class to validate against for name [${againstClass}]")
def constrainedProperties = dc.constrainedProperties.collect { k,v -> return v }
def appliedConstraints = []
constrainedProperties.each {
appliedConstraints += it.collect{ it.appliedConstraints }
}
appliedConstraints = appliedConstraints.flatten()
def fieldValidations = [:]
appliedConstraints.each {
def validateType = CONSTRAINT_TYPE_MAP[it.name]
if(validateType) {
if(fieldValidations[validateType]) {
fieldValidations[validateType] << it
}
else {
fieldValidations[validateType] = [it]
}
}
}
out << '<script type="text/javascript">\n'
def scriptNameUtil = "org/apache/commons/validator/javascript/validateUtilities.js"
def inStreamUtil = getClass().classLoader.getResourceAsStream(scriptNameUtil)
if(inStreamUtil) {
out << inStreamUtil.text
}
fieldValidations.each { k,v ->
def validateType = k
if(validateType) {
def validateTypes = [validateType]
if(validateType.contains(",")) {
validateTypes = validateType.split(",")
}
validateTypes.each { vt ->
// import required script
def scriptName = "org/apache/commons/validator/javascript/validate" + vt.substring(0,1).toUpperCase() + vt.substring(1) + ".js"
def inStream = getClass().classLoader.getResourceAsStream(scriptName)
if(inStream) {
out << inStream.text
}
switch(vt) {
case 'mask':
out << "function ${form}_mask() {";
break;
case 'intRange':
out << "function ${form}_intRange() {";
break;
case 'floatRange':
out << "function ${form}_floatRange() {";
break;
case 'maxLength':
out << "function ${form}_maxlength() {";
break;
case 'minLength':
out << "function ${form}_minlength() {";
break;
case 'email':
out << "function ${form}_email() {";
break;
case 'creditCard':
out << "function ${form}_creditCard() {";
break;
case 'required':
out << "function ${form}_required() {";
break;
default:
out << "function ${form}_${vt}() {";
break;
}
//out << "function ${form}_${vt.toLowerCase()}() {"
v.each { constraint ->
out << "this.${constraint.propertyName} = new Array("
out << "'${constraint.propertyName}'," // the field
//out << '"Test message"' // TODO: Resolve the actual message
def clazzName = againstClass?.toLowerCase()
out << getMessage(vt, clazzName, constraint.propertyName, constraint)
switch(vt) {
case 'mask': out << ",function() { return '${constraint.regex}'; }";break;
case 'intRange': out << ",function() { if(arguments[0]=='min') return ${constraint.range.from}; else return ${constraint.range.to} }";break;
case 'floatRange': out << ",function() { if(arguments[0]=='min') return ${constraint.range.from}; else return ${constraint.range.to} }";break;
case 'maxLength':
if (!isSizeConstraint(constraint)) {
out << ",function() {return ${constraint.maxSize};}";
} else {
out << ",function() {return ${constraint.range.to};}";
}
break;
case 'minLength':
if (!isSizeConstraint(constraint)) {
out << ",function() {return ${constraint.minSize};}";
} else {
out << ",function() {return ${constraint.range.from};}";
}
break;
}
out << ');\n'
}
out << "}\n"
}
}
}
out << 'function validateForm(form) {\n'
fieldValidations.each { k,v ->
def splittedTypes = splitType(k);
for (spt in splittedTypes) {
def validateType = spt.substring(0,1).toUpperCase() + spt.substring(1)
out << "if(!validate${validateType}(form)) return false;\n"
}
}
out << 'return true;\n';
out << '}\n'
out << "document.forms['${attrs['form']}'].onsubmit = function() {return validateForm(this);}\n"
out << '</script>'
}
//添加
def static messageMap = [
"mask" : "{0}不符合正则表达式[{1}]",
"creditCard" : "{0}不是一个合法的信用卡号",
"email" : "{0}不是一个合法的Email地址",
"intRange" : "{0}不在{1}到{2}之内",
"floatRange" : "{0}不在{1}到{2}之内",
"maxLength" : "{0}的长度大于{1}",
"minLength" : "{0}的长度小于{1}",
"required" : "{0}不能为空",
]
//添加
def static splitType(type) {
if (type.indexOf(",") >= 0) {
StringTokenizer st = new StringTokenizer(type, ",");
def retList = []
while (st.hasMoreTokens()) {
retList << st.nextToken();
}
return retList;
} else {
return [type];
}
}
def static isSizeConstraint(constraint) {
if (constraint.getClass().getName() == "org.codehaus.groovy.grails.validation.ConstrainedProperty\$SizeConstraint")
return true
else return false
}
//添加
def getMessage(vt, clazzName, propertyName, constraint) {
def cn = CP.getChinesePropertyName(clazzName + "." + propertyName)
if (!cn) cn = propertyName
Object[] args = new Object[3]
args[0] = cn
def omsg = messageMap[vt]
if (!omsg) omsg = "存在未知错误"
def msg = "";
switch(vt) {
case 'email':
break
case 'creditCard':
break
case 'mask':
args[1] = constraint.regex
break
case 'intRange':
args[1] = constraint.range.from
args[2] = constraint.range.to
break
case 'floatRange':
args[1] = constraint.range.from
args[2] = constraint.range.to
break;
case 'maxLength':
if (!isSizeConstraint(constraint)) {
args[1] = constraint.maxSize
} else {
args[1] = constraint.range.to
}
break
case 'minLength':
if (!isSizeConstraint(constraint)) {
args[1] = constraint.minSize
} else {
args[1] = constraint.range.from
}
break
}
return "\"" + MessageFormat.format(omsg, args) + "\""
}
}
[/code]