Grails的Javascript验证DIY
程序员文章站
2022-03-14 12:44:49
...
看到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()这样的东东,可以说是个比较严重的错误,所以这里顺便更改了一下
第四个地方: 看看这段代码
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) + "\"" } }
下一篇: Grails的Ajax