JSF 复杂表单 - 绑定错误
Posted
技术标签:
【中文标题】JSF 复杂表单 - 绑定错误【英文标题】:JSF Complex form - Binding Error 【发布时间】:2011-07-12 02:23:49 【问题描述】:我有一个带有一些 inputs 的表单,它后面有一个 ViewScoped *ManagedBean*。在表单的底部,我有一个用于保存数据的命令按钮。表单上有一个数据表,可以通过在输入中输入数据并单击另一个CommandButton来添加新项目。当用户填写所有输入并将任何项目添加到数据表时,他/她可以单击保存按钮。但是我在控制添加按钮和更新数据模型时遇到了一些问题。当我将按钮的 immadiate 设置为 true 时,输入的值不会更新,当我将其设置为 false 时,表单上的其余输入将发生验证错误!!!
如果有帮助就写代码:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD Xhtml 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
template="./templates/master.xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns="http://www.w3.org/1999/xhtml">
<ui:define name="windowTitle">
#lbls.registerWaggon
</ui:define>
<ui:define name="sectionTitle">
<h:panelGroup layout="block" styleClass="sectionTitle">
<h:graphicImage library="img" name="railways.png"/>
</h:panelGroup>
</ui:define>
<ui:define name="right">
<ui:include src="templates/railwaysright.xhtml"/>
</ui:define>
<ui:define name="extraCSS">
<h:outputStylesheet library="css" name="persiancalendar.css"/>
<h:outputStylesheet library="css" name="grid.css"/>
</ui:define>
<ui:define name="extraJS">
<h:outputScript library="js" name="lib/persiancalendar.js"/>
</ui:define>
<ui:define name="content">
<h:panelGroup rendered="#!current.hasLoggedIn()">
<h:panelGroup layout="block" styleClass="warningBox">
<h:outputText value="#app.youHaveNotLoggedIn"/>
<br/>
<h:link value="#lbls.login" outcome="login"/>
</h:panelGroup>
</h:panelGroup>
<p:panel rendered="#current.hasLoggedIn() and requestWaggon.isViewable()">
<f:facet name="header">
<h:outputText value="#lbls.registerWaggonLong" />
</f:facet>
<h:form id="frmRequest">
<h:panelGrid columns="3" footerClass="buttons">
<h:outputText value="#lbls.number:"/>
<h:inputText id="number" label="#lbls.number" styleClass="ltr" value="#requestWaggon.request.number" readonly="true" />
<h:message for="number" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.requestDate:"/>
<h:inputText styleClass="ltr" id="date" label="#lbls.requestDate" value="#requestWaggon.request.date" required="true" readonly="true">
<f:converter converterId="ir.khorasancustoms.DateConverter"/>
</h:inputText>
<h:message for="date" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.nameOfMaterialOwner:"/>
<h:inputText id="ownerName" label="#lbls.nameOfMaterialOwner" value="#requestWaggon.request.fullName" required="true" readonly="true"/>
<h:message for="ownerName" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.companyName:"/>
<h:inputText id="companyName" label="#lbls.companyName" value="#requestWaggon.request.companyName" required="true" readonly="true"/>
<h:message for="companyName" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.nameOfMaterial:"/>
<h:inputText id="nameOfMaterial" label="#lbls.nameOfMaterial" value="#requestWaggon.request.materialName" required="true" readonly="true"/>
<h:message for="nameOfMaterial" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.materialWeight:"/>
<h:panelGroup>
<h:inputText id="materialWeight" styleClass="ltr" label="#lbls.materialWeight" value="#requestWaggon.request.materialWeight" required="true" style="min-width: 0px; width: 60px" readonly="true"/>
<h:outputText value=" #requestWaggon.request.weightUnit"/>
</h:panelGroup>
<h:message for="materialWeight" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.senderAddress:"/>
<h:inputText id="senderAddress" label="#lbls.senderAddress" value="#requestWaggon.request.address" required="true" style="width: 350px;" readonly="true"/>
<h:message for="senderAddress" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.receiverAddress (#lbls.country):"/>
<h:outputText value="#requestWaggon.request.country"/>
<h:message for="country" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.station:"/>
<h:inputText id="station" label="#lbls.station" value="#requestWaggon.request.station" required="true" readonly="true"/>
<h:message for="station" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.loadingDate:"/>
<h:panelGroup>
<h:inputText styleClass="ltr" id="loadingDate" label="#lbls.loadingDate" value="#requestWaggon.request.loadingDate" required="true" readonly="true">
<f:converter converterId="ir.khorasancustoms.DateConverter"/>
</h:inputText>
</h:panelGroup>
<h:message for="loadingDate" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.boundryStation:"/>
<h:outputText value="#requestWaggon.request.bountryStation"/>
<h:message for="boundryStation" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value=""/>
<h:outputText value=""/>
<h:outputText value=""/>
<h:outputText value="#lbls.confirmDate:"/>
<h:panelGroup>
<h:inputText styleClass="date ltr" id="confirmDate" label="#lbls.confirmDate" value="#requestWaggon.request.confirmDate" required="true">
<f:converter converterId="ir.khorasancustoms.DateConverter"/>
</h:inputText>
<input type="button" value="..." onclick="displayDatePicker('frmRequest:confirmDate', this);" class="datePicker"/>
</h:panelGroup>
<h:message for="confirmDate" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.confirm:" styleClass="b"/>
<h:selectOneMenu value="#requestWaggon.request.confirm">
<f:selectItem/>
<f:selectItems value="#searchRequest.allConfirms" var="confirm" itemLabel="#searchRequest.confirmCaption(confirm)" itemValue="#confirm"/>
</h:selectOneMenu>
<h:message for="confirm" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.description:"/>
<h:inputText id="description" label="#lbls.description" value="#requestWaggon.request.confirmDescription" required="false" style="width: 350px;"/>
<h:message for="description" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
<h:outputText value="#lbls.waggonType:"/>
<h:selectOneMenu id="waggonType" label="#lbls.waggonType" value="#requestWaggon.request.waggonType" required="true">
<f:selectItem/>
<f:selectItems value="#requestWaggon.waggonTypes"/>
<f:converter converterId="ir.khorasancustoms.CatalogValueFixedConverter"/>
</h:selectOneMenu>
<h:message for="waggonType" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>
**<h:outputText value="#lbls.specialWaggonNumber:"/>
<h:panelGroup>
<h:inputText id="specialWaggonNumber" label="#lbls.specialWaggonNumber" binding="#requestWaggon.waggonNumberComponent" />
<h:commandButton value="#lbls.add" action="#requestWaggon.addWaggon" immediate="true"/>
</h:panelGroup>
<h:message for="specialWaggonNumber" infoClass="info" warnClass="warning" errorClass="error" fatalClass="fatal"/>**
<h:outputText value="#lbls.waggons:"/>
<h:dataTable value="#requestWaggon.waggonsDataModel" var="waggon" columnClasses="index,,action" styleClass="grid" headerClass="title" rowClasses="two,three,one">
<h:column>
<f:facet name="header">#lbls.index</f:facet>
<h:outputText value="#searchRequest.datamodel.rowIndex + 1"/>
</h:column>
<h:column>
<f:facet name="header">#lbls.number</f:facet>
<h:outputText value="#waggon.number"/>
</h:column>
<h:column>
<f:facet name="header">#lbls.action</f:facet>
<h:commandLink action="delete">
<h:graphicImage styleClass="nb" title="#lbls.delete" library="img" name="delete.png"/>
<f:param name="id" value="#waggon.id"/>
</h:commandLink>
</h:column>
</h:dataTable>
<f:facet name="footer">
<h:button outcome="searchrequest" value="#lbls.cancel" rendered="#requestWaggon.id ne null"/>
<h:commandButton action="#requestWaggon.doNew" value="#lbls.new" rendered="#requestWaggon.request.id ne null"/>
<h:commandButton action="#requestWaggon.save" value="#lbls.ok"/>
</f:facet>
</h:panelGrid>
<h:outputScript>
focusElement('frmRequest:confirmDate');
</h:outputScript>
</h:form>
<f:facet name="footer">
<h:messages styleClass="boxMessages" layout="table" infoClass="infoBox" warnClass="warningBox" errorClass="errorBox" fatalClass="errorBox" globalOnly="true"/>
<h:link outcome="searchrequest" value="#lbls.searchRequestWaggon"/>
</f:facet>
</p:panel>
<p:panel rendered="#current.hasLoggedIn() and !requestWaggon.isViewable()">
<h:panelGroup layout="block" styleClass="warningBox">
<h:outputText value="#app.accessDenied" />
</h:panelGroup>
</p:panel>
</ui:define>
</ui:composition>
豆
@ManagedBean(name = "requestWaggon")
@ViewScoped
public class RequestWaggonBean
//<editor-fold defaultstate="collapsed" desc="FIELDS">
private Logger logger;
@ManagedProperty(value = "#current")
private CurrentSessionBean current;
private RequestWaggon request;
private Set<CatalogValue> weightUnits;
private Set<CatalogValue> countries;
private Set<CatalogValue> boundryStations;
private Set<CatalogValue> waggonTypes;
private Integer id;
@ManagedProperty(value = "#searchRequest")
private SearchRequestBean searchRequest;
private Integer newWaggonNumber;
private HtmlInputText waggonNumberComponent;
private DataModel<Waggon> waggonsDataModel;
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="CONSTRUCTORS">
public RequestWaggonBean()
logger = LogUtil.getLogger(RequestWaggonBean.class);
request = new RequestWaggon();
ExternalContext exContext = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest httpRequest = (HttpServletRequest) exContext.getRequest();
String strId = httpRequest.getParameter("id");
try
id = Integer.parseInt(strId);
catch (Exception ex)
logger.fatal(ex);
@PostConstruct
public void init()
request.setDate(current.getDate());
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
ResourceBundle app = ResourceBundle.getBundle("application");
try
//session.beginTransaction();
if (id != null)
request = (RequestWaggon) session.get(RequestWaggon.class, id);
Query query = session.createQuery("from CatalogGroup as catalogGroup where catalogGroup.englishTitle = :englishTitle");
query.setParameter("englishTitle", "WeightUnit");
CatalogGroup weightUnit = (CatalogGroup) query.uniqueResult();
weightUnits = weightUnit.getValues();
query.setParameter("englishTitle", "Country");
CatalogGroup country = (CatalogGroup) query.uniqueResult();
countries = country.getValues();
query.setParameter("englishTitle", "BoundryStation");
CatalogGroup boundryStation = (CatalogGroup) query.uniqueResult();
boundryStations = boundryStation.getValues();
query.setParameter("englishTitle", "WaggonType");
CatalogGroup waggonType = (CatalogGroup) query.uniqueResult();
waggonTypes = waggonType.getValues();
//session.getTransaction().commit();
catch (Exception ex)
logger.fatal(ex);
Transaction tx = session.getTransaction();
if (tx.isActive())
tx.rollback();
String message = app.getString("databaseConnectionFailed");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, message, message));
finally
session.close();
Waggon[] arrayWaggons = new Waggon[request.getWaggons().size()];
arrayWaggons = request.getWaggons().toArray(arrayWaggons);
waggonsDataModel = new ArrayDataModel<Waggon>(arrayWaggons);
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="METHODS">
public void save()
boolean canSave = false;
String cantSaveMessage = "";
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
ResourceBundle app = ResourceBundle.getBundle("application");
try
session.beginTransaction();
int number = 0;
if (request.getNumber() == null)
number = RequestWaggon.nextNumber();
if (request.getNumber() != null || number > 0)
if (request.getNumber() == null)
request.setNumber(number);
canSave = isInsertable();
cantSaveMessage = app.getString("insertDenied");
else
canSave = isEditable();
cantSaveMessage = app.getString("editDenied");
if (canSave)
session.saveOrUpdate(request);
RequestWaggonHistory history = new RequestWaggonHistory(request, current.getUser(), number > 0 ? 'I' : 'U', current.getIP());
session.save(history);
session.getTransaction().commit();
String message = app.getString("savedSuccessfully");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));
else
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, cantSaveMessage, cantSaveMessage));
else
logger.fatal("Getting next number failed!");
String message = app.getString("databaseConnectionFailed");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, message, message));
catch (Exception ex)
logger.fatal(ex);
Transaction tx = session.getTransaction();
if (tx.isActive())
tx.rollback();
String message = app.getString("databaseConnectionFailed");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, message, message));
finally
session.close();
public void addWaggon(ValueChangeEvent event)
ResourceBundle app = ResourceBundle.getBundle("application");
String strNewWaggonNumber = event.getNewValue().toString();
Integer newWaggonNumber = Integer.parseInt(strNewWaggonNumber);
if (newWaggonNumber == null || newWaggonNumber <= 0)
String message = app.getString("EnterWaggonNumber");
FacesContext.getCurrentInstance().addMessage("frmRequest:specialWaggonNumber", new FacesMessage(FacesMessage.SEVERITY_ERROR, message, message));
else
Waggon newWaggon = new Waggon();
newWaggon.setNumber(newWaggonNumber);
request.getWaggons().add(newWaggon);
Waggon[] arrayWaggons = new Waggon[request.getWaggons().size()];
arrayWaggons = request.getWaggons().toArray(arrayWaggons);
waggonsDataModel = new ArrayDataModel<Waggon>(arrayWaggons);
public boolean isViewable()
return current.isViewable();
public boolean isDeletable()
return current.isDeletable() && request.getWaggons().isEmpty();
public boolean isInsertable()
return current.isInsertable();
public boolean isEditable()
return current.isEditable() && request.getConfirm() == null;
public void doNew()
request = new RequestWaggon();
request.setDate(current.getDate());
public String delete()
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
ResourceBundle app = ResourceBundle.getBundle("application");
try
session.beginTransaction();
session.delete(request);
RequestWaggonHistory history = new RequestWaggonHistory(request, current.getUser(), 'D', current.getIP());
session.save(history);
session.getTransaction().commit();
searchRequest.search();
String message = app.getString("deletedSuccessfully");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));
catch (ConstraintViolationException ex)
String message = app.getString("constraintViolation");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, message, message));
catch (Exception ex)
logger.fatal(ex);
Transaction tx = session.getTransaction();
if (tx.isActive())
tx.rollback();
String message = app.getString("databaseConnectionFailed");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, message, message));
finally
session.close();
return "searchrequest";
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="PROPERTIES">
public RequestWaggon getRequest()
return request;
public void setRequest(RequestWaggon requestWaggon)
this.request = requestWaggon;
public CurrentSessionBean getCurrent()
return current;
public void setCurrent(CurrentSessionBean current)
this.current = current;
public Set<CatalogValue> getWeightUnits()
return weightUnits;
public void setWeightUnits(Set<CatalogValue> weightUnits)
this.weightUnits = weightUnits;
public Logger getLogger()
return logger;
public void setLogger(Logger logger)
this.logger = logger;
public Set<CatalogValue> getCountries()
return countries;
public void setCountries(Set<CatalogValue> countries)
this.countries = countries;
public Set<CatalogValue> getBoundryStations()
return boundryStations;
public void setBoundryStations(Set<CatalogValue> boundryStations)
this.boundryStations = boundryStations;
public Integer getId()
return id;
public void setId(Integer id)
this.id = id;
public SearchRequestBean getSearchRequest()
return searchRequest;
public void setSearchRequest(SearchRequestBean searchRequest)
this.searchRequest = searchRequest;
public Set<CatalogValue> getWaggonTypes()
return waggonTypes;
public void setWaggonTypes(Set<CatalogValue> waggonTypes)
this.waggonTypes = waggonTypes;
public Integer getNewWaggonNumber()
return newWaggonNumber;
public void setNewWaggonNumber(Integer newWaggonNumber)
this.newWaggonNumber = newWaggonNumber;
public DataModel<Waggon> getWaggonsDataModel()
return waggonsDataModel;
public void setWaggonsDataModel(DataModel<Waggon> waggonsDataModel)
this.waggonsDataModel = waggonsDataModel;
public HtmlInputText getWaggonNumberComponent()
return waggonNumberComponent;
public void setWaggonNumberComponent(HtmlInputText waggonNumberComponent)
this.waggonNumberComponent = waggonNumberComponent;
//</editor-fold>
【问题讨论】:
【参考方案1】:h:commandButton
提交整个表单,所有元素都将被验证。您可以使用 ajax 进行部分提交。请尝试以下操作:
给你的数据表一个 id 值。所以你可以从你的 commandButton 中引用它:
<h:panelGroup id="tableWrapper">
<h:datatable id="mytable" value="#requestWaggon.waggonsDataModel" ..>
...
</h:datatable>
</h:panelGroup>
然后在你的命令按钮中放一个 f:ajax>。 render
和 execute
属性必须包含数据表的 id;不需要immediate=true
:
<h:commandButton value="#lbls.add" action="#requestWaggon.addWaggon">
<f:ajax render="tableWrapper" execute="mytable"/>
</h:commandButton>
【讨论】:
这是一个很好的解决方案,但是 JSF 中也有一些经典的(非 Ajax)解决方案。以上是关于JSF 复杂表单 - 绑定错误的主要内容,如果未能解决你的问题,请参考以下文章
基于 JSF 表单的身份验证 + 托管 Bean 登录不起作用