Java 如何在Vaadin中正确验证日期字段?
我在验证日期字段时遇到一些问题。 在我的应用程序中,我有一个带有DateField属性的表,用户应该能够通过按编辑按钮来编辑该表。我还有一个提交字段的OK按钮和一个丢弃字段的cancel按钮 以下是我想要实现的目标(当然,必须遵循一些简洁的规则):Java 如何在Vaadin中正确验证日期字段?,java,validation,vaadin,vaadin7,datefield,Java,Validation,Vaadin,Vaadin7,Datefield,我在验证日期字段时遇到一些问题。 在我的应用程序中,我有一个带有DateField属性的表,用户应该能够通过按编辑按钮来编辑该表。我还有一个提交字段的OK按钮和一个丢弃字段的cancel按钮 以下是我想要实现的目标(当然,必须遵循一些简洁的规则): 首先,日期只能更改为9999-12-31之前的当前日期 其次,我更希望验证是动态的(当您键入时) 原始日期(进入编辑模式时已在表中的日期)可以是任何日期,您应该能够按原样提交这些日期,即使它们是在过去 如果您将日期更改为无效日期(您仍然可以“手动”
- 首先,日期只能更改为9999-12-31之前的当前日期
- 其次,我更希望验证是动态的(当您键入时)
- 原始日期(进入编辑模式时已在表中的日期)可以是任何日期,您应该能够按原样提交这些日期,即使它们是在过去
- 如果您将日期更改为无效日期(您仍然可以“手动”执行此操作,即直接在字段中,而不是在日期选择器中)或在日期字段中输入无效字符,则应显示错误图标并显示一条消息,在输入有效日期之前不允许您提交更改
- 如果将日期更改为无效日期(显示错误图标),然后更改为有效日期,则错误图标应消失
- 允许“原始日期”-确定
- 允许更改为有效日期-确定
- 当更改为无效日期(可以“手动”完成,不使用日期选择器)并在字段中按enter键时,该字段将立即重置为原始日期,但错误图标仍显示-不确定
- 输入无效字符(可以“手动”完成,不使用日期选择器)并在字段中按enter键时,提交时将抛出NPE,并且不会显示错误图标-不确定
- 当更改为无效日期并在字段中按enter键,然后返回到有效日期并在字段中按enter键时,错误图标仍然存在-不确定
- 更改为无效日期并按OK(即提交())时,字段首先重置为原始日期,并且更改(即对字段没有任何更改)已提交-不确定
createField(){
// some more code up here...
if (propertyId.equals("Valid From")) {
dField.setImmediate(true);
dField.setRangeStart(new Date());
dField.setRangeEnd(dateUtil.getDate(9999, 12, 31));
dField.setDateOutOfRangeMessage("Date out of range!");
@SuppressWarnings({ "unchecked", "rawtypes" })
TableDataValidatingWrapper<TextField> wField = new TableDataValidatingWrapper(dField);
return wField;
}
// some more code down here...
}
public class TableDataValidatingWrapper<T> extends CustomField<T> {
private static final long serialVersionUID = 1L;
protected Field<T> delegate;
public TableDataValidatingWrapper(final Field<T> delegate) {
this.delegate = delegate;
if (delegate instanceof DateField) {
final DateField dateField = (DateField) delegate;
dateField.setCaption("");
dateField.setImmediate(true);
dateField.setInvalidAllowed(false);
dateField.setInvalidCommitted(true);
dateField.setValidationVisible(false);
dateField.addValueChangeListener(new ValueChangeListener() {
private static final long serialVersionUID = 1L;
@Override
public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
try {
dateField.validate();
dateField.setValidationVisible(false);
} catch (InvalidValueException ive) {
//handle exception
} catch (Exception e) {
//handle exception
}
}
});
}
}
//some other overridden methods here...
}
createField(){
//这里还有一些代码。。。
if(propertyId.equals(“生效日期”)){
dField.setImmediate(真);
dField.setRangeStart(新日期());
dField.setRangeEnd(dateUtil.getDate(9999,12,31));
dField.setDateOutOfRangeMessage(“日期超出范围!”);
@SuppressWarnings({“unchecked”,“rawtypes”})
TableDataValidatingWrapper wField=新的TableDataValidatingWrapper(dField);
返回wField;
}
//下面还有一些代码。。。
}
。。。这是我的包装纸:
createField(){
// some more code up here...
if (propertyId.equals("Valid From")) {
dField.setImmediate(true);
dField.setRangeStart(new Date());
dField.setRangeEnd(dateUtil.getDate(9999, 12, 31));
dField.setDateOutOfRangeMessage("Date out of range!");
@SuppressWarnings({ "unchecked", "rawtypes" })
TableDataValidatingWrapper<TextField> wField = new TableDataValidatingWrapper(dField);
return wField;
}
// some more code down here...
}
public class TableDataValidatingWrapper<T> extends CustomField<T> {
private static final long serialVersionUID = 1L;
protected Field<T> delegate;
public TableDataValidatingWrapper(final Field<T> delegate) {
this.delegate = delegate;
if (delegate instanceof DateField) {
final DateField dateField = (DateField) delegate;
dateField.setCaption("");
dateField.setImmediate(true);
dateField.setInvalidAllowed(false);
dateField.setInvalidCommitted(true);
dateField.setValidationVisible(false);
dateField.addValueChangeListener(new ValueChangeListener() {
private static final long serialVersionUID = 1L;
@Override
public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
try {
dateField.validate();
dateField.setValidationVisible(false);
} catch (InvalidValueException ive) {
//handle exception
} catch (Exception e) {
//handle exception
}
}
});
}
}
//some other overridden methods here...
}
公共类TableDataValidatingWrapper扩展了CustomField{
私有静态最终长serialVersionUID=1L;
受保护领域代表;
公共TableDataValidatingWrapper(最终字段委托){
this.delegate=委托;
if(委托实例of DateField){
最终日期字段DateField=(DateField)委托;
dateField.setCaption(“”);
dateField.setImmediate(true);
dateField.setInvalidAllowed(false);
dateField.setInvalidCommitted(true);
dateField.setValidationVisible(false);
dateField.addValueChangeListener(新的ValueChangeListener(){
私有静态最终长serialVersionUID=1L;
@覆盖
public void valueChange(com.vaadin.data.Property.ValueChangeEvent事件){
试一试{
dateField.validate();
dateField.setValidationVisible(false);
}catch(InvalidValueException){
//处理异常
}catch(异常e){
//处理异常
}
}
});
}
}
//这里还有一些其他被重写的方法。。。
}
有点复杂,但我希望它能工作(在Vaadin 7中)。我使用一些Apache Commons和Joda Time helper方法。
也许需要一些定制
public class MyDateField extends CustomField<Date> {
private static final long serialVersionUID = 1L;
private static final DateTimeFormatter DTF;
static {
DTF = DateTimeFormat.forPattern("yyyy-MM-dd"); // set timezone if needed
}
private TextField tf = new TextField();
private DateField df = new DateField();
private Date original;
private Date minDay = new Date();
private Date maxDay = new DateTime(9999, 12, 31, 23, 59).toDate();
private boolean isInnerChange;
private Date convertedDate;
@Override
protected Component initContent() {
tf.setConverter(InnerConverter.INSTANCE);
tf.setTextChangeEventMode(TextChangeEventMode.EAGER); // or LAZY
tf.addTextChangeListener(new TextChangeListener() {
private static final long serialVersionUID = 1L;
@Override
public void textChange(TextChangeEvent event) {
int pos = tf.getCursorPosition();
if (isValid(event.getText())) {
df.setComponentError(null);
isInnerChange = true;
df.setValue(convertedDate);
} else {
df.setComponentError(InnerErrorMessage.INSTANCE);
}
tf.setCursorPosition(pos);
}
});
df.setStyleName("truncated-date-field");
df.addValueChangeListener(new Property.ValueChangeListener() {
private static final long serialVersionUID = 1L;
@Override
public void valueChange(Property.ValueChangeEvent event) {
if (!isInnerChange) {
Date d = df.getValue();
df.setComponentError(isValid(d) ? null : InnerErrorMessage.INSTANCE);
tf.setValue(d == null ? "" : DTF.print(d.getTime()));
}
isInnerChange = false;
}
});
return new HorizontalLayout(tf, df);
}
@Override
public void setPropertyDataSource( @SuppressWarnings("rawtypes") Property newDS) {
tf.setPropertyDataSource(newDS);
if (newDS != null && getType().isAssignableFrom(newDS.getType())) {
original = (Date) newDS.getValue();
} else {
original = null;
}
df.setValue(original);
}
@Override
public void commit() throws SourceException, InvalidValueException {
ErrorMessage em = df.getComponentError();
if (em != null) {
throw new InvalidValueException(em.getFormattedHtmlMessage());
}
tf.commit();
}
@Override
public Class<? extends Date> getType() {
return Date.class;
}
private boolean isValid(String s) {
s = StringUtils.trimToNull(s);
if (s == null) {
convertedDate = null;
return true;
}
try {
return isValid(DTF.parseDateTime(s).toDate());
} catch (Exception e) {
return false;
}
}
private boolean isValid(Date d) {
if (d == null || DateUtils.truncatedEquals(original, d, Calendar.DAY_OF_MONTH)) {
convertedDate = d;
return true;
}
if (DateUtils.truncatedCompareTo(minDay, d, Calendar.DAY_OF_MONTH) <= 0
&& DateUtils.truncatedCompareTo(maxDay, d, Calendar.DAY_OF_MONTH) >= 0) {
convertedDate = d;
return true;
}
return false;
}
// other methods if needed
private static class InnerErrorMessage implements ErrorMessage {
private static final long serialVersionUID = 1L;
private static final InnerErrorMessage INSTANCE = new InnerErrorMessage();
@Override
public String getFormattedHtmlMessage() {
return "Invalid date!";
}
@Override
public ErrorLevel getErrorLevel() {
return ErrorLevel.ERROR;
}
private Object readResolve() {
return INSTANCE; // preserves singleton property
}
}
private static class InnerConverter implements Converter<String, Date> {
private static final long serialVersionUID = 1L;
private static final InnerConverter INSTANCE = new InnerConverter();
@Override
public Date convertToModel(String value, Class<? extends Date> targetType, Locale locale)
throws ConversionException {
String s = StringUtils.trimToNull(value);
if (s == null) {
return null;
}
try {
return DTF.parseDateTime(s).toDate();
} catch (Exception e) {
throw new ConversionException(e);
}
}
@Override
public String convertToPresentation(Date value, Class<? extends String> targetType, Locale locale)
throws ConversionException {
return value == null ? "" : DTF.print(value.getTime());
}
@Override
public Class<Date> getModelType() {
return Date.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
private Object readResolve() {
return INSTANCE; // preserves singleton property
}
}
}
你有什么解决方案吗?更新:Joda Time项目现在处于维护模式。它的创建者斯蒂芬·科尔伯恩(Stephen Colebourne)继续领导JSR 310,在java 8及更高版本中添加内置的java.time类。