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>。 renderexecute 属性必须包含数据表的 id;不需要immediate=true

<h:commandButton value="#lbls.add" action="#requestWaggon.addWaggon">
  <f:ajax render="tableWrapper" execute="mytable"/>
</h:commandButton>

【讨论】:

这是一个很好的解决方案,但是 JSF 中也有一些经典的(非 Ajax)解决方案。

以上是关于JSF 复杂表单 - 绑定错误的主要内容,如果未能解决你的问题,请参考以下文章

JSF selectManyListbox 显示值绑定错误

登录后重定向错误(Java EE w/JSF)

发生验证错误时,在JSF中突出显示inputText

基于 JSF 表单的身份验证 + 托管 Bean 登录不起作用

一个表单发布后敲除绑定会引发错误并且该帖子是从另一个表单启动的?

意外错误转发或重定向到登录页面[重复]