我们需要向用户显示两个p:日历组件,分别表示开始日期和结束日期。两个约会时间都有日期、小时和分钟。PrimeFaces具有完善的mindate
, maxdate
, minHour
, maxHour
, minMinute
和minMinute
属性。
现在的要求是:
不可能将开始日期时间设置为大于或等于结束日期时间的任何值。不可能将结束日期时间设置为小于或等于结束日期时间的任何值。
下面的等式应该成立:
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>
下面是一个min/max方法的例子(mindate of end-date):
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;
}
这基本上只表示,如果已经设置了开始日期,并且开始日期和结束日期当前相同,则将可选择的结束时间(end-date的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,并且应该允许选择日历日期2013-04-20,只要结束时间以某种方式调整为至少12:35。
但是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
添加到每个p:calendar
的update
属性中,以便在更新期间调用各自的getter (getSelectedBeginDate()
和getSelectedEndDate
+ min/max限制器)。
@this
会混淆p:日历组件,使时间滑块只能滑动一次。随后的滑动器事件被简单地忽略,表现失常。
Q
- 你通常是如何解决这个问题的?
- 使用
p:remoteCommand
是实现我们想要的方式吗?
可选Q :
- 为什么PrimeFaces p:日历没有被实现来提供一个单一的minDateTime和maxDateTime,这可能会解决手头的问题?
我敢打赌我描述的这种情况以前已经解决了。如果您能描述一下您设法解决这个问题的方法(甚至分享一个部分解决方案),我将非常感激。
前言:
我不使用JSF,但是有一些事情可能会引导您回到您想要的地方:
a)当使用标准日历中dateTime的日期部分时,考虑使用:
someCalendar.set(Calendar.MILLISECOND, 0)
b)考虑使用joda-time,因为在许多情况下,为了正确性、性能和易用性,它似乎经常被推荐(在这里、这里和许多其他地方)而不是标准库。
c)确保您的bean作用域在每个ajax调用中幸存(不重定向,只发送标准回发等),并且每个事件处理程序都获得faces上下文(例如。FacesContext facesContext = FacesContext.getCurrentInstance();
)
d) mindate
和类似的可能不像你期望的那样工作,我不希望自动行为可以如此容易地插入。
当这些选项都不可用时,你必须自己用你现有的东西来做:
philosophy/UX:我要做的第一件事是消除对这对日期的安排或观点的期望。不要把这对货币对看作是一个暴露或期望在时间轴上有一个方向的向量。
换句话说,
start
或from
日期是否总是小于或早于end
或to
日期?不可以,就像查询历史数据,或者对尚未发生或已经发生的事件应用更正一样。这个含义很容易让用户搞不清他们是要"回到"还是"向前"(也很容易让你自己搞不清)。相反,我将把它们之间有时间段的一对日期简单地视为声明间隔的
a pair of dates
或range
或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);
}
强迫精度:第三,即使在分辨率范围内,我们如何解决值的差异?(我们的应用程序被赋予的精度超过了它可以处理、期望或需要的精度)。
long diff = value % dateTimeResolutionInMs;
截断:
return value - diff;
最接近(w/bias):
return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;
Others:有许多其他策略可以将值缩小或扩展到首选分辨率或精度
附录:至于让回发/Ajax调用返回一个视图,其中包含您对calendar
元素触发的事件所期望的值,如果前言中的注释没有给您带来任何帮助,并且您确信bean已正确注册和识别,那么您可能希望将该关注点分离到一个新问题。您可能有一些浏览器/浏览器版本特定的问题,导致不期望的行为,并且像其他任何事情一样,存在已知和未知的问题。