扩展Grails的datePicker标签
本文所述代码实现基于Grails 1.1 以上版本。
---------------------------------------------------------------------------------------------------------------------------
Grails的datePicker标签是一个常用的标签,它有一个属性years,使用这个属性可以指定列表列出的年份,可以是范围,也可以是多选。这个属性在你需要限制选择范围的时候非常有用,但是如果你还要限制月份,日期,小时,分,秒这些时间单位呢?
datePicker标签并没有提供相应的属性,那我们只能自己扩展
了。我们希望他有months, days, hours, minuts这些属性,并且和years具有相同的设置方式。
修改步骤:
首先将代码导入IDE中(省略过程),在目录 src\groovy\org\codehaus\groovy\grails\plugins\web\taglib 下找到FormTagLib.groovy(奇怪为什么是在plugins下的),打开并查找"def datePicker ="下面的内容就是datePicker标签的实现代码,阅读代码可以发现这么一段
// create year select if (precision >= PRECISION_RANKINGS["year"]) { out.println "<select name=\"${name}_year\" id=\"${id}_year\">" if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, '') out.println() } for (i in years) { out.println "<option value=\"${i}\"" if (i == year) { out.println " selected=\"selected\"" } out.println ">${i}</option>" } out.println '</select>' }
这段代码就是标签生成年份选择框的,关键的部分是
for (i in years)
循环生成选择项,它遍历的是years这个变量,往前查找这个变量是这么得到的
def years = attrs['years']
看到这儿我们就知道了,要实现前面的扩展其实很容易,依葫芦画瓢照样子做就是了。
在years后面添加如下代码:
def months = attrs['months'] def days = attrs['days'] def hours = attrs['hours'] def minutes = attrs['minutes']
考虑属性不设置为空的情况,需要给变量指定默认值,添加以下代码:
if (months == null) { months = 1..12 } if (days == null) { days = 1..31 } if (hours == null) { hours = 0..23 } if (minutes == null) { minutes = 0..59 }
然后修改生成每个时间段选择项的代码,如days:将
for (i in 1..31) {
修改为
for (i in days) {
其他都类似,除了月份需要特殊处理,循环时输出时判断,修改如下:
dfs.months.eachWithIndex {m, i -> if (m) { def monthIndex = i + 1 if(months.contains(monthIndex)){ out << "<option value=\"${monthIndex}\"" if (month == i) out << " selected=\"selected\"" out << '>' out << m out.println '</option>' } } }
最后,用ant执行build.xml中的jar,它会编译并打包至dist目录下,其中grails-web-1.1.jar就是扩展后的标签所在的jar,将这个包复制到Grails安装目录的的lib目录下,覆盖原来的jar文件,就可以使用自己扩展的datePicker标签了。
--------------------------------------------------------
附件:最后修改的代码如下
/** * A simple date picker that renders a date as selects * eg. <g:datePicker name="myDate" value="${new Date()}" /> */ def datePicker = {attrs -> def xdefault = attrs['default'] if (xdefault == null) { xdefault = new Date() } else if (xdefault.toString() != 'none') { if (xdefault instanceof String) { xdefault = DateFormat.getInstance().parse(xdefault) }else if(!(xdefault instanceof Date)){ throwTagError("Tag [datePicker] requires the default date to be a parseable String or a Date") } } else { xdefault = null } def value = attrs['value'] if (value.toString() == 'none') { value = null } else if (!value) { value = xdefault } def name = attrs['name'] def id = attrs['id'] ? attrs['id'] : name def noSelection = attrs['noSelection'] if (noSelection != null) { noSelection = noSelection.entrySet().iterator().next() } def years = attrs['years'] def months = attrs['months'] def days = attrs['days'] def hours = attrs['hours'] def minutes = attrs['minutes'] final PRECISION_RANKINGS = ["year": 0, "month": 10, "day": 20, "hour": 30, "minute": 40] def precision = (attrs['precision'] ? PRECISION_RANKINGS[attrs['precision']] : (grailsApplication.config.grails.tags.datePicker.default.precision ? PRECISION_RANKINGS["${grailsApplication.config.grails.tags.datePicker.default.precision}"] : PRECISION_RANKINGS["minute"])) def day def month def year def hour def minute def dfs = new java.text.DateFormatSymbols(RCU.getLocale(request)) def c = null if (value instanceof Calendar) { c = value } else if (value != null) { c = new GregorianCalendar(); c.setTime(value) } if (c != null) { day = c.get(GregorianCalendar.DAY_OF_MONTH) month = c.get(GregorianCalendar.MONTH) year = c.get(GregorianCalendar.YEAR) hour = c.get(GregorianCalendar.HOUR_OF_DAY) minute = c.get(GregorianCalendar.MINUTE) } if (years == null) { def tempyear if (year == null) { // If no year, we need to get current year to setup a default range... ugly def tempc = new GregorianCalendar() tempc.setTime(new Date()) tempyear = tempc.get(GregorianCalendar.YEAR) } else { tempyear = year } years = (tempyear - 100)..(tempyear + 100) } if (months == null) { months = 1..12 } if (days == null) { days = 1..31 } if (hours == null) { hours = 0..23 } if (minutes == null) { minutes = 0..59 } out << "<input type=\"hidden\" name=\"${name}\" value=\"struct\" />" // create day select if (precision >= PRECISION_RANKINGS["day"]) { out.println "<select name=\"${name}_day\" id=\"${id}_day\">" if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, '') out.println() } for (i in days) { out.println "<option value=\"${i}\"" if (i == day) { out.println " selected=\"selected\"" } out.println ">${i}</option>" } out.println '</select>' } // create month select if (precision >= PRECISION_RANKINGS["month"]) { out.println "<select name=\"${name}_month\" id=\"${id}_month\">" if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, '') out.println() } dfs.months.eachWithIndex {m, i -> if (m) { def monthIndex = i + 1 if(months.contains(monthIndex)){ out << "<option value=\"${monthIndex}\"" if (month == i) out << " selected=\"selected\"" out << '>' out << m out.println '</option>' } } } out.println '</select>' } // create year select if (precision >= PRECISION_RANKINGS["year"]) { out.println "<select name=\"${name}_year\" id=\"${id}_year\">" if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, '') out.println() } for (i in years) { out.println "<option value=\"${i}\"" if (i == year) { out.println " selected=\"selected\"" } out.println ">${i}</option>" } out.println '</select>' } // do hour select if (precision >= PRECISION_RANKINGS["hour"]) { out.println "<select name=\"${name}_hour\" id=\"${id}_hour\">" if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, '') out.println() } for (i in hours) { def h = '' + i if (i < 10) h = '0' + h out << "<option value=\"${h}\" " if (hour == h.toInteger()) out << "selected=\"selected\"" out << '>' << h << '</option>' out.println() } out.println '</select> :' // If we're rendering the hour, but not the minutes, then display the minutes as 00 in read-only format if (precision < PRECISION_RANKINGS["minute"]) { out.println '00' } } // do minute select if (precision >= PRECISION_RANKINGS["minute"]) { out.println "<select name=\"${name}_minute\" id=\"${id}_minute\">" if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, '') out.println() } for (i in minutes) { def m = '' + i if (i < 10) m = '0' + m out << "<option value=\"${m}\" " if (minute == m.toInteger()) out << "selected=\"selected\"" out << '>' << m << '</option>' out.println() } out.println '</select>' } }