39

我们需要向用户呈现两个 p:calendar 组件,分别代表一个开始日期和结束日期。两个日期时间都有日期、小时和分钟。PrimeFaces 具有可用的完美mindatemaxdateminHourmaxHourminMinuteminMinute属性。

现在的要求是:

无法将开始日期时间设置为大于或等于结束日期时间的任何值。不可能将结束日期时间设置为小于或等于结束日期时间的任何值。

以下等式应该成立:

begin datetime < end datetime

现在我们尝试了以下 JSF:

<p:calendar id="begin-date"
            value="#{debugManager.selectedBeginDate}"
            mindate="#{debugManager.minBeginDate}"
            maxdate="#{debugManager.maxBeginDate}"
            maxHour="#{debugManager.maxBeginHour}"
            maxMinute="#{debugManager.maxBeginMinute}"
            pattern="yyyy-MM-dd HH:mm"
            showButtonPanel="true"
            readonlyInput="true"
            navigator="true"
            showOn="button"
            required="true">
    <p:ajax event="dateSelect" update="end-date" />
</p:calendar>

<p:calendar id="end-date"
            value="#{debugManager.selectedEndDate}"
            mindate="#{debugManager.minEndDate}"
            minHour="#{debugManager.minEndHour}"
            minMinute="#{debugManager.minEndMinute}"
            pattern="yyyy-MM-dd HH:mm"
            showButtonPanel="true"
            readonlyInput="true"
            navigator="true"
            showOn="button">
    <p:ajax event="dateSelect" update="begin-date" />
</p:calendar>

这是一个示例性的最小/最大方法(结束日期的 mindate):

public Date getMinEndDate()
{
    return this.getSelectedBeginDate();
}

如您所见,最短结束日期是当前 AJAX 选择的开始日期。正确设置结束日期不允许将开始日期设置为超过结束日期。

当将时间纳入方程式时,问题就开始了……

由于 p:calendar 的接口有单独的方法,bean 必须提供逻辑:

public int getMinEndHour()
{
    Date selectedBeginDate = this.getSelectedBeginDate();
    Date selectedEndDate = this.getSelectedEndDate();

    if ( selectedBeginDate != null && DateUtil.isSameDay( selectedBeginDate, selectedEndDate ) )
    {
        return DateUtil.getHourOf( selectedBeginDate );
    }

    return ComplianceConstants.DEFAULT_COMPLIANCE_CASE_MIN_END_HOUR;
}

这基本上只说明如果已设置开始日期并且开始日期和结束日期当前相同,则将可选择的结束时间(minHour结束日期)限制为开始时间。

操作:

Set the begin datetime to 2013-04-20 12:34 (legit)
Set the end   datetime to 2013-04-22 00:00 (legit)

现在结束日期的时间位于 00:00,只要将结束时间调整为至少 12:35,就应该允许选择日历日期 2013-04-20。

但是 p:calendar 组件现在无法知道这一点

sets the end datetime to 2013-04-20 00:00 (legit, but false)

...

现在的问题是,当用户在日历中按下某个新的结束日期时,mindate/maxdate 属性不能限制用户点击与开始日期相同的日期。如果现在结束日期时间恰好在同一个开始日期的时间之前,那么我们无能为力(这是错误的)。

现在的后续问题是用户能够关闭日历并只需按下提交按钮即可将虚假数据插入数据库。当然,可以/应该运行验证器,但我们必须以某种方式在没有验证器的情况下实现这一点

如果日期在同一天,我们接下来尝试的是修补setSelectedBeginDate( Date selectedBeginDate )setSelectedEndDate( Date selectedEndDate )调整设置时间部分的方法。java.util.Date像这样的东西:

public void adjustSelectedEndDate()
{
    if ( this.selectedEndDate != null )
    {
        this.log.infov( "adjustSelectedEndDate: b-hour = {0}, e-hour = {1}", DateUtil.getHourOf( this.selectedBeginDate ), DateUtil.getHourOf( this.selectedEndDate ) );

        if ( DateUtil.isSameDay( this.selectedBeginDate, this.selectedEndDate ) &&
            ( DateUtil.getHourOf( this.selectedEndDate ) < DateUtil.getHourOf( this.selectedBeginDate ) ) ||
              DateUtil.getHourOf( this.selectedEndDate ) == DateUtil.getHourOf( this.selectedBeginDate ) && DateUtil.getMinuteOf( this.selectedEndDate ) <= DateUtil.getMinuteOf( this.selectedBeginDate ) )
        {
            this.log.info( "Adjusting selected end date!" );

            this.selectedEndDate = DateUtil.addOneMinuteTo( DateUtil.copyTime( this.selectedBeginDate, this.selectedEndDate ) );
        }
    }
}

这要求我们添加@this每个update属性,p:calendar以便在更新期间调用相应的 getter(getSelectedBeginDate()和+ 最小/最大限制器)。getSelectedEndDate

然而,在更新上放置一个@this会混淆 p:calendar 组件,使时间滑块只能滑动一次。随后的滑块事件将被简单地忽略,表现为损坏。

Q的

  • 您通常如何解决这个问题?
  • 是用p:remoteCommand的方式来达到我们想要的吗?

可选问

  • 为什么没有实施 PrimeFaces p:calendar 来提供单个 minDateTime 和 maxDateTime,这可能会解决手头的问题?

我敢打赌,我描述的这种情况之前已经解决了。如果您能描述您设法解决此问题的方法(甚至分享部分解决方案),我将不胜感激。

4

1 回答 1

1

前言:

我不与 JSF 合作,但有几件事可能会引导你回到你想去的地方:

a) 在标准日历中仅使用 dateTime 的日期部分时,请考虑使用:

someCalendar.set(Calendar.MILLISECOND, 0)

b) 考虑使用joda-time ,因为在许多情况下,为了正确性、性能和易用性,它似乎经常被推荐(herehere许多其他地方)而不是标准库。

c) 确保您的 bean 范围在每个 ajax 调用(不重定向,仅发送标准回发等)中幸存,并且每个事件处理程序都在获取 faces 上下文(例如FacesContext facesContext = FacesContext.getCurrentInstance();

d)mindate之类的可能不会像您期望的那样工作,而且我不认为自动行为可以如此容易地插入。

当这些选项不可用时,您必须自己完成所有操作:

哲学/用户体验: 我要做的第一件事是从这对日期中消除对安排或视角的期望。不要将该对视为在时间轴上暴露或期望方向的向量。

  • 换句话说,startfrom日期总是小于或早于endto日期吗?不,正如查询历史数据或对尚未发生或已经发生的事件应用更正所看到的那样?

    这种含义很容易使用户混淆他们是“返回”还是“前进”(并且很容易混淆您自己)。相反,我会将一对在它们之间有一个时间段的日期视为公正而简单的那个a pair of dates或声明一个间隔的 arange或 a period,并根据任何随后选择的值推断它们在时间线上的相对位置。通过这种方式,您可以尊重日期从不相等的各自和固有的要求,左边总是在左边,右边总是在右边。

我们无法推断“开始”或“从”是什么意思,但我们可以推断出一些意义和相对关系:在时间轴上的右、左和中间。注意:在进行任何计算或比较之前,始终将日期解析为 UTC。

long oneDateValue = oneDate.toUtc().toMilliseconds();
long anotherDateValue = anotherDate.toUtc().toMilliseconds();

long right = max (oneDateValue, anotherDateValue);
long left = min (oneDateValue, anotherDateValue);

评估精度: 在使用任何语言处理一系列日期时,我会考虑的第二件事类似于处理浮点数的方式。对于比较,不要比较是否相等,而是将增量与“可接受的错误级别”进行比较。换句话说,应用程序实际上只关心一定程度的精度,因此请确保仅捕获和考虑该精度:

const int dateTimeResolutionInMs = 86400000; // milliseconds per day

public bool areEssentiallySame(long left, long right) {

   // the difference between right and left is less than our precision 
   // requires, thus dates are effectively the same
   return (right - left < dateTimeResolutionInMs);
}

强制精度: 第三,即使在分辨率范围内,我们如何解决值的差异?(Out 应用程序的精确度超出了它可以处理或预期或需要的程度)。

long diff = value % dateTimeResolutionInMs;

  1. 截短: return value - diff;

  2. 最近(w/bias): return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;

  3. 其他:还有许多其他策略可以将值缩小或扩大到首选分辨率或精度

附录: 至于获取回发/Ajax 调用以返回具有您对calendar元素触发的事件的期望值的视图,如果前言中的注释没有,您可能希望将该问题分离到一个新问题带您到任何地方,并且您确定您的 bean 已正确注册和识别。 您可能会遇到一些特定于浏览器/浏览器版本的问题,这些问题会导致不良行为,并且与其他任何事情一样,存在已知和未知的问题。

于 2013-07-10T04:55:23.740 回答