In the form below a person can be entered along with cars. The user can e.g. remove cars by clicking the "minus" commandButton an a row of the dataTable. However,
- when the "minus" commandButton is immediate=false then the removal of cars does not work since validation kicks in (lastname of person is required) which is working as expected.
- when it is immediate=true, then the save does not work properly. Example: three cars are in the list and "Ford" (being second in the list) is removed. The removal seems to work fine and the browser correctly reflects the change. When saving is triggered via the savePerson - method, however, it shows that the last car in the list (which was Toyota) was removed and not Ford. What can I do to have the changes being reflected when saving the person?
Log-output from bean below:
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>
Bean
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;
}
}