0

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;
    }
}
Mike
  • 121
  • 2
  • 8
  • The question is a bit ambiguous, which one is acceptable as dupe for you? http://stackoverflow.com/q/12571759 or http://stackoverflow.com/q/8370675 – BalusC Jan 07 '16 at 20:19
  • Please feel free to delete that post if you think the question/answer does not provide new info additionally to the links you posted in your comment and thanks for you help!!! – Mike Jan 08 '16 at 16:28

1 Answers1

0

Thanks to the links BalusC provided me with I implemented the below solution.

immediate = true is wrong in any case since when entering new cars and then deleting one of them the model (components) needs to be updated with the form data. Otherwise, the data already filled in the form would get lost when adding/removing a car (plus/minus commandButton).

That means that all input components of the form need to be processed, including the required component "last" in order to update the model with that data. To avoid validation of the required component "last" when a car is added/removed and to validate only when the save button is klicked the solution of [How to let validation depend on the pressed button? is used.

That last point, however, seems to work only when ajax="false" is not specified. If so, then param[save.clientId] seems always to be empty. Only when ajax="true" (which is the default) will the parameter have a value.

Change jsf page:

<?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>
Community
  • 1
  • 1
Mike
  • 121
  • 2
  • 8