在控制器中从params绑定Grails日期

42

为什么在Grails控制器中通过params从视图中提取日期很困难?

我不想像这样手动提取日期:

instance.dateX = parseDate(params["dateX_value"])//parseDate is from my helper class

我只想使用 instance.properties = params

模型中的类型是 java.util.Date,在参数中包含所有信息:[dateX_month: 'value', dateX_day: 'value', ...]

我在网上搜索了一下,但没有找到相关信息。我希望 Grails 1.3.0 能够帮助,但仍然是同样的问题。

我不能也不愿意相信需要手动提取日期!


请注意,在最近的(2.0.x)Grails版本中存在一个影响日期绑定的错误:http://jira.grails.org/browse/GRAILS-9165 - Ken Liu
为了后人留存:请注意,Joda Time插件会自动实现数据绑定。 - Charles Wood
6个回答

89

Grails版本 >= 2.3

Config.groovy中的一个设置定义了绑定参数到Date时应用程序范围内使用的日期格式

grails.databinding.dateFormats = [
        'MMddyyyy', 'yyyy-MM-dd HH:mm:ss.S', "yyyy-MM-dd'T'hh:mm:ss'Z'"
]

grails.databinding.dateFormats 中指定的格式将按照列表中包含它们的顺序进行尝试。

您可以使用 @BindingFormat 覆盖这些应用程序范围内的格式,以适用于单个命令对象。

import org.grails.databinding.BindingFormat

class Person { 
    @BindingFormat('MMddyyyy') 
    Date birthDate 
}

Grails版本低于2.3

我不相信需要手动提取日期!

你的固执得到了回报,早在Grails 1.3之前就可以直接绑定日期了。步骤如下:

(1)创建一个类为您的日期格式注册编辑器

import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry
import org.springframework.beans.propertyeditors.CustomDateEditor
import java.text.SimpleDateFormat

public class CustomDateEditorRegistrar implements PropertyEditorRegistrar {

    public void registerCustomEditors(PropertyEditorRegistry registry) {

        String dateFormat = 'yyyy/MM/dd'
        registry.registerCustomEditor(Date, new CustomDateEditor(new SimpleDateFormat(dateFormat), true))
    }
}

(2)grails-app/conf/spring/resources.groovy中注册以下bean,使Grails能够识别该日期编辑器:

beans = {
    customPropertyEditorRegistrar(CustomDateEditorRegistrar)
}

(3) 现在,当你以 yyyy/MM/dd 格式在一个名为 foo 的参数中发送日期时,它将自动绑定到一个名为 foo 的属性,使用以下任一方法:

myDomainObject.properties = params

或者
new MyDomainClass(params)

我收到了以下警告:project/src/java/CustomEditorRegistrar.java使用或覆盖了已弃用的API。有没有一种“新”的方法来做这件事? - zoran119
1
@zoran119 我不知道。如果你要使用Java而不是Groovy,你需要对上面的代码进行一些小的更改。 - Dónal
抱歉Don,我在错误的问题中发布了评论 :( 我的评论是针对这个问题的:https://dev59.com/53NA5IYBdhLWcg3wcNTF - zoran119
@Don,非常好的答案,但对于OP的观点,这似乎对于一个日期来说是荒谬的。也许如果您想要非标准的日期格式,这很有用。然而,似乎应该有一个默认格式,可以在不添加任何自定义内容的情况下正常工作。 - Josh Petitt
我认为值得一提的是,并非所有这些解决方案都适用于您希望国际化的应用程序。如果您需要,您可以使用@BindingFormat(code ='default.date.format')或查看@mpccolorado的答案。 - Cookalino
显示剩余2条评论

15

Grails 2.1.1中的params有了新方法,可轻松进行空值安全解析。

def val = params.date('myDate', 'dd-MM-yyyy')
// or a list for formats
def val = params.date('myDate', ['yyyy-MM-dd', 'yyyyMMdd', 'yyMMdd']) 
// or the format read from messages.properties via the key 'date.myDate.format'
def val = params.date('myDate')

在文档这里找到它。


3
我认为这里的阅读是从“params”中获取一个日期,而不是“绑定一个日期”。 - Dónal
似乎是最好的答案。但我测试了一下,发现了一个问题。如果我没有在messages.properties中添加代码,Grails会引发“找不到代码date.myDate.format下的消息”的异常。我认为引擎应该在引发这样的异常之前搜索通用格式(default.date.format)。 - Cléssio Mendes

12

Grails版本 >= 3.x

您可以在application.yml中设置日期格式,遵循以下语法:

grails:
  databinding:
    dateFormats:
      - 'dd/MM/yyyy'
      - 'dd/MM/yyyy HH:mm:ss'
      - 'yyyy-MM-dd HH:mm:ss.S'
      - "yyyy-MM-dd'T'hh:mm:ss'Z'"
      - "yyyy-MM-dd HH:mm:ss.S z"
      - "yyyy-MM-dd'T'HH:mm:ssX"

2

@Don 感谢您上面的回答。

这里有一个替代自定义编辑器的方法,它首先检查日期时间,然后检查日期格式。

Groovy很好用,只需要为Java添加分号即可。

import java.text.DateFormat
import java.text.ParseException
import org.springframework.util.StringUtils
import java.beans.PropertyEditorSupport

class CustomDateTimeEditor extends PropertyEditorSupport {
    private final java.text.DateFormat dateTimeFormat
    private final java.text.DateFormat dateFormat
    private final boolean allowEmpty

    public CustomDateTimeEditor(DateFormat dateTimeFormat, DateFormat dateFormat, boolean allowEmpty) {
        this.dateTimeFormat = dateTimeFormat
        this.dateFormat = dateFormat
        this.allowEmpty = allowEmpty
    }

    /**
     * Parse the Date from the given text, using the specified DateFormat.
     */
    public void setAsText(String   text) throws IllegalArgumentException   {
        if (this.allowEmpty && !StringUtils.hasText(text)) {
            // Treat empty String as null value.
            setValue(null)
        }
        else {
            try {
                setValue(this.dateTimeFormat.parse(text))
            }
            catch (ParseException dtex) {
                try {
                    setValue(this.dateFormat.parse(text))
                }
                catch ( ParseException dex ) {
                    throw new IllegalArgumentException  ("Could not parse date: " + dex.getMessage() + " " + dtex.getMessage() )
                }
            }
        }
    }

    /**
     * Format the Date as String, using the specified DateFormat.
     */
    public String   getAsText() {
        Date   value = (Date) getValue()
        return (value != null ? this.dateFormat.format(value) : "")
    }
}

2

您是否尝试使用过任何Grails日期选择器插件?

我使用过日历插件,并且有良好的使用体验。

(当使用日历插件时)当您提交日期选择请求时,可以自动将查询参数绑定到要用请求填充的领域对象。

例如:

new DomainObject(params)

您也可以这样解析“yyyy/MM/dd”格式的日期字符串...
new Date().parse("yyyy/MM/dd", "2010/03/18")

2
我认为明确调用 new Date.parse() 正是他想避免的。据我所知,如果您直接绑定参数,仍然需要注册自定义日期编辑器(如我的回复中所述)。否则,数据绑定器怎么可能知道哪个字段是月份,哪个是年份(例如)?我同意日历插件是可用的日期选择器插件中最好的。 - Dónal
肯定要选择参数绑定的方法。不过,new Date().parse() 在其他地方也非常有用。 - tinny
我现在的方法非常糟糕。首先,我会从参数中删除日期值,像这样:(params.remove("datefield")) 然后我会做类似这样的事情:instance.datefield = hlpr.exDate(params["datefield_value"] as String)。我知道这听起来很奇怪,但这似乎是目前唯一可行的方法... - nils petersohn

1

Grails版本 >= 2.3

一种支持本地化的版本,用于将字符串转换为日期

在src/groovy中:

package test

import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest
import org.grails.databinding.converters.ValueConverter
import org.springframework.context.MessageSource
import org.springframework.web.servlet.LocaleResolver

import javax.servlet.http.HttpServletRequest
import java.text.SimpleDateFormat

class StringToDateConverter implements ValueConverter {
    MessageSource messageSource
    LocaleResolver localeResolver

    @Override
    boolean canConvert(Object value) {
        return value instanceof String
    }

    @Override
    Object convert(Object value) {
        String format = messageSource.getMessage('default.date.format', null, "dd/MM/yyyy", getLocale())
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format)
        return simpleDateFormat.parse(value)
    }

    @Override
    Class<?> getTargetType() {
        return Date.class
    }

    protected Locale getLocale() {
        def locale
        def request = GrailsWebRequest.lookup()?.currentRequest
        if(request instanceof HttpServletRequest) {
            locale = localeResolver?.resolveLocale(request)
        }
        if(locale == null) {
            locale = Locale.default
        }
        return locale
    }
}

在conf/spring/resources.groovy中:
beans = {
    defaultDateConverter(StringToDateConverter){
        messageSource = ref('messageSource')
        localeResolver = ref('localeResolver')
    }
}

豆子的名字"defaultDateConverter"非常重要 (可以覆盖默认日期转换器)

很好的解决方案,可以跨应用程序更改所有数据绑定,并且支持本地化。我很惊讶Grails默认情况下不会这样做,因为它符合约定优于配置的原则。 - Cookalino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接