模型已更新但更改丢失
Model is updated but changes get lost
在下面的表格中,可以输入人和汽车。用户可以,例如单击数据表一行中的 "minus" 命令按钮删除汽车。然而,
- 当 "minus" commandButton 为 immediate=false 时,由于验证开始(需要人员的姓氏),因此无法移除汽车预期。
- 当它是 immediate=true, 时保存不能正常工作。示例:列表中有三辆车,"Ford"(列表中的第二个)被删除。删除似乎工作正常,浏览器正确反映了更改。然而,当通过 savePerson 方法触发保存时,它显示列表中的最后一辆汽车(丰田)被删除,而不是福特。 我怎样做才能在救人时反映出变化?
来自以下 bean 的日志输出:
removing car Car{brand='Ford'}
remaining car Car{brand='Porsche'}
remaining car Car{brand='Toyota'}
saving person
saving car Car{brand='Porsche'}
saving car Car{brand='Ford'}
JSF
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition template="/META-INF/templates/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:define name="content">
<h:form id="formChanges">
<p:accordionPanel id="accPanel">
<p:tab title="Cars">
<p:panelGrid columns="2">
<f:facet name="header">Person</f:facet>
<p:outputLabel value="Last" for="last"/>
<p:inputText id="last" value="#{table.person.last}" required="true"/>
<p:outputLabel value="First" for="first"/>
<p:inputText id="first" value="#{table.person.first}"/>
</p:panelGrid>
<p:dataTable var="car" value="#{table.person.cars}">
<p:column>
<f:facet name="header">Brand</f:facet>
<p:inputText value="#{car.brand}"/>
</p:column>
<p:column>
<f:facet name="header">Options</f:facet>
<p:commandButton value="#{msgs.minus}"
action="#{table.removeCar(car)}"
immediate="true"
update="formChanges:accPanel"/>
</p:column>
</p:dataTable>
<p:commandButton value="#{msgs.plus}" action="#{table.addCar}"
immediate="true" update="formChanges:accPanel"/>
<p:spacer height="10"/>
<p:commandButton id="save" value="#{msgs.save}" icon="ui-icon-check"
action="#{table.savePerson}" ajax="false"/>
</p:tab>
</p:accordionPanel>
</h:form>
</ui:define>
</ui:composition>
豆子
import com.google.common.collect.Lists;
import de.dgr.question.Car;
import de.dgr.question.Person;
import org.slf4j.Logger;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
import java.io.Serializable;
import java.util.List;
@ManagedBean(name = "table")
@ViewScoped
public class TableController implements Serializable {
@Inject
private Logger log;
private Person person;
@PostConstruct
public void init() {
List<Car> cars = Lists.newArrayList(new Car("Porsche"), new Car("Ford"), new Car("Toyota"));
person = new Person();
person.setCars(cars);
}
public String savePerson() {
log.info("-> saving person");
for (Car car : person.getCars()) {
log.info("saving car {}", car);
}
return null;
}
public String removeCar(final Car carToDelete) {
log.info("removing car {}", carToDelete);
person.getCars().remove(carToDelete);
for (Car car : person.getCars()) {
log.info("remaining car {}", car);
}
return null;
}
public String addCar() {
log.info("adding empty car");
person.getCars().add(new Car());
return null;
}
public Person getPerson() {
return person;
}
public void setPerson(final Person person) {
this.person = person;
}
}
感谢 BalusC 提供的链接,我实现了以下解决方案。
immediate = true 在任何情况下都是错误的,因为当输入新车然后删除其中一辆时,模型(组件)需要使用表单数据进行更新。否则,当 adding/removing 一辆汽车 (plus/minus commandButton).
时,表格中已经填写的数据将会丢失
这意味着需要处理表单的所有输入组件,包括必需的组件 "last",以便使用该数据更新模型。为了避免在汽车 added/removed 时验证所需的组件 "last" 并仅在点击保存按钮时验证,使用 [How to let validation depend on the pressed button? 的解决方案。
然而,最后一点似乎仅在未指定 ajax="false" 时有效。如果是这样,那么 param[save.clientId] 似乎总是空的。只有当 ajax="true"(这是默认值)时参数才会有值。
更改 jsf 页面:
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition template="/META-INF/templates/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:define name="content">
<h:form id="formChanges" prependId="false">
<p:accordionPanel id="accPanel" prependId="false">
<p:tab title="Cars">
<h:panelGroup id="panelNewData">
<p:panelGrid columns="2">
<f:facet name="header">Person</f:facet>
<p:outputLabel value="Last" for="last"/>
<p:inputText id="last" value="#{table.person.last}"
required="#{not empty param[save.clientId]}" />
<p:outputLabel value="First" for="first"/>
<p:inputText id="first" value="#{table.person.first}"/>
</p:panelGrid>
<p:dataTable var="car" value="#{table.person.cars}">
<p:column>
<f:facet name="header">Brand</f:facet>
<p:inputText value="#{car.brand}"/>
</p:column>
<p:column>
<f:facet name="header">Options</f:facet>
<p:commandButton value="#{msgs.minus}"
action="#{table.removeCar(car)}"
process="panelNewData"
update="panelNewData"/>
</p:column>
</p:dataTable>
<p:commandButton value="#{msgs.plus}" action="#{table.addCar}"
process="panelNewData" update="accPanel"/>
<p:spacer height="10"/>
<p:commandButton id="save" binding="#{save}" value="#{msgs.save}"
icon="ui-icon-check" action="#{table.savePerson}"
update="accPanel"/>
</h:panelGroup>
</p:tab>
</p:accordionPanel>
</h:form>
</ui:define>
</ui:composition>
在下面的表格中,可以输入人和汽车。用户可以,例如单击数据表一行中的 "minus" 命令按钮删除汽车。然而,
- 当 "minus" commandButton 为 immediate=false 时,由于验证开始(需要人员的姓氏),因此无法移除汽车预期。
- 当它是 immediate=true, 时保存不能正常工作。示例:列表中有三辆车,"Ford"(列表中的第二个)被删除。删除似乎工作正常,浏览器正确反映了更改。然而,当通过 savePerson 方法触发保存时,它显示列表中的最后一辆汽车(丰田)被删除,而不是福特。 我怎样做才能在救人时反映出变化?
来自以下 bean 的日志输出:
removing car Car{brand='Ford'}
remaining car Car{brand='Porsche'}
remaining car Car{brand='Toyota'}
saving person
saving car Car{brand='Porsche'}
saving car Car{brand='Ford'}
JSF
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition template="/META-INF/templates/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:define name="content">
<h:form id="formChanges">
<p:accordionPanel id="accPanel">
<p:tab title="Cars">
<p:panelGrid columns="2">
<f:facet name="header">Person</f:facet>
<p:outputLabel value="Last" for="last"/>
<p:inputText id="last" value="#{table.person.last}" required="true"/>
<p:outputLabel value="First" for="first"/>
<p:inputText id="first" value="#{table.person.first}"/>
</p:panelGrid>
<p:dataTable var="car" value="#{table.person.cars}">
<p:column>
<f:facet name="header">Brand</f:facet>
<p:inputText value="#{car.brand}"/>
</p:column>
<p:column>
<f:facet name="header">Options</f:facet>
<p:commandButton value="#{msgs.minus}"
action="#{table.removeCar(car)}"
immediate="true"
update="formChanges:accPanel"/>
</p:column>
</p:dataTable>
<p:commandButton value="#{msgs.plus}" action="#{table.addCar}"
immediate="true" update="formChanges:accPanel"/>
<p:spacer height="10"/>
<p:commandButton id="save" value="#{msgs.save}" icon="ui-icon-check"
action="#{table.savePerson}" ajax="false"/>
</p:tab>
</p:accordionPanel>
</h:form>
</ui:define>
</ui:composition>
豆子
import com.google.common.collect.Lists;
import de.dgr.question.Car;
import de.dgr.question.Person;
import org.slf4j.Logger;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
import java.io.Serializable;
import java.util.List;
@ManagedBean(name = "table")
@ViewScoped
public class TableController implements Serializable {
@Inject
private Logger log;
private Person person;
@PostConstruct
public void init() {
List<Car> cars = Lists.newArrayList(new Car("Porsche"), new Car("Ford"), new Car("Toyota"));
person = new Person();
person.setCars(cars);
}
public String savePerson() {
log.info("-> saving person");
for (Car car : person.getCars()) {
log.info("saving car {}", car);
}
return null;
}
public String removeCar(final Car carToDelete) {
log.info("removing car {}", carToDelete);
person.getCars().remove(carToDelete);
for (Car car : person.getCars()) {
log.info("remaining car {}", car);
}
return null;
}
public String addCar() {
log.info("adding empty car");
person.getCars().add(new Car());
return null;
}
public Person getPerson() {
return person;
}
public void setPerson(final Person person) {
this.person = person;
}
}
感谢 BalusC 提供的链接,我实现了以下解决方案。
immediate = true 在任何情况下都是错误的,因为当输入新车然后删除其中一辆时,模型(组件)需要使用表单数据进行更新。否则,当 adding/removing 一辆汽车 (plus/minus commandButton).
时,表格中已经填写的数据将会丢失这意味着需要处理表单的所有输入组件,包括必需的组件 "last",以便使用该数据更新模型。为了避免在汽车 added/removed 时验证所需的组件 "last" 并仅在点击保存按钮时验证,使用 [How to let validation depend on the pressed button? 的解决方案。
然而,最后一点似乎仅在未指定 ajax="false" 时有效。如果是这样,那么 param[save.clientId] 似乎总是空的。只有当 ajax="true"(这是默认值)时参数才会有值。
更改 jsf 页面:
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition template="/META-INF/templates/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:define name="content">
<h:form id="formChanges" prependId="false">
<p:accordionPanel id="accPanel" prependId="false">
<p:tab title="Cars">
<h:panelGroup id="panelNewData">
<p:panelGrid columns="2">
<f:facet name="header">Person</f:facet>
<p:outputLabel value="Last" for="last"/>
<p:inputText id="last" value="#{table.person.last}"
required="#{not empty param[save.clientId]}" />
<p:outputLabel value="First" for="first"/>
<p:inputText id="first" value="#{table.person.first}"/>
</p:panelGrid>
<p:dataTable var="car" value="#{table.person.cars}">
<p:column>
<f:facet name="header">Brand</f:facet>
<p:inputText value="#{car.brand}"/>
</p:column>
<p:column>
<f:facet name="header">Options</f:facet>
<p:commandButton value="#{msgs.minus}"
action="#{table.removeCar(car)}"
process="panelNewData"
update="panelNewData"/>
</p:column>
</p:dataTable>
<p:commandButton value="#{msgs.plus}" action="#{table.addCar}"
process="panelNewData" update="accPanel"/>
<p:spacer height="10"/>
<p:commandButton id="save" binding="#{save}" value="#{msgs.save}"
icon="ui-icon-check" action="#{table.savePerson}"
update="accPanel"/>
</h:panelGroup>
</p:tab>
</p:accordionPanel>
</h:form>
</ui:define>
</ui:composition>