Ответ 1
Что я делаю неправильно
Выполнение проверки вне контекста данных в нестандартном JSF-способе. Данные строки доступны только , в то время как вы (или JSF) выполняете итерацию по данным datatable. Там физически только один компонент <p:calendar>
в каждом столбце, который имеет несколько разных состояний в зависимости от текущей круглой итерации, проходящей по дате. Эти состояния недоступны, если вы не выполняете повторную проверку данных. Вы получите только null
как значение.
Технически, с вашим различным подходом к проверке до сих пор вы должны использовать метод visitTree()
для компонента UIData
и выполнять задание в VisitCallback
реализация. Это приведет к переходу через данные.
Например,
dataTable.visitTree(VisitContext.createVisitContext(), new VisitCallback() {
@Override
public VisitResult visit(VisitContext context, UIComponent component) {
// Check if component is instance of <p:calendar> and collect its value by its ID.
return VisitResult.ACCEPT;
}
});
Это только неуклюже. Это дает вам каждую строку, вам нужно будет поддерживать и проверять индекс строки самостоятельно и собирать значения. Обратите внимание, что вызов UIInput#setValid()
также должен выполняться внутри реализации VisitCallback
.
или я должен использовать совершенно другую стратегию?
Да, используйте обычный Validator
стандартный способ JSF. Вы можете передать один компонент как атрибут другого компонента.
например.
<p:column>
<p:calendar binding="#{startDateComponent}" id="startDate" required="true" value="#{item.start}" pattern="MM/dd/yyyy hh:mm a"/>
</p:column>
<p:column >
<p:calendar id="endDate" required="true" value="#{item.end}" pattern="MM/dd/yyyy hh:mm a">
<f:validator validatorId="dateRangeValidator" />
<f:attribute name="startDateComponent" value="#{startDateComponent}" />
</p:calendar>
</p:column>
с
@FacesValidator("dateRangeValidator")
public class DateRangeValidator implements Validator {
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null) {
return; // Let required="true" handle.
}
UIInput startDateComponent = (UIInput) component.getAttributes().get("startDateComponent");
if (!startDateComponent.isValid()) {
return; // Already invalidated. Don't care about it then.
}
Date startDate = (Date) startDateComponent.getValue();
if (startDate == null) {
return; // Let required="true" handle.
}
Date endDate = (Date) value;
if (startDate.after(endDate)) {
startDateComponent.setValid(false);
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Start date may not be after end date.", null));
}
}
}
Поскольку оба компонента находятся в одной строке, startDateComponent
будет "автоматически" возвращать правильное значение на getValue()
каждый раз, когда вызывается этот валидатор. Обратите внимание, что этот валидатор также можно использовать за пределами данных, тогда как ваш первоначальный подход не является.
В качестве альтернативы вы можете использовать OmniFaces <o:validateOrder>
как полное решение. Его пример showcase даже показывает этот конкретный пример использования <p:calendar>
компонентов внутри <p:dataTable>
.