3

I am currently trying to do a simple list of inputs with adding/removing primefaces p:commandButton.

I am using PrimeFaces 6.2 on Glassfish 4.1.1 with Mojarra 2.2.12.

ExampleBean.java

package /* myPackage */;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javafx.util.Pair;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named(value = "exampleBean")
@SessionScoped
public class ExampleBean implements Serializable {

    private List<Pair<Integer, Integer>> list;

    @PostConstruct
    public void init() {
        list = new ArrayList<>();
        addNewItem();
    }

    public void addNewItem() {
        list.add(new Pair<>(1, 300));
    }

    public List<Pair<Integer, Integer>> getList() {
        return list;
    }

    public void setList(List<Pair<Integer, Integer>> list) {
        this.list = list;
    }
}

example.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:ui="http://java.sun.com/jsf/facelets" 
      xmlns:p="http://primefaces.org/ui" 
      xmlns:jsf="http://xmlns.jcp.org/jsf">

    <h:head>
        <title>Example</title>
        <meta charset="utf-8" />
    </h:head>

    <h:body>
        <h:form id="example-form">
            <div jsf:id="example-container">
                <ui:repeat value="#{exampleBean.list}" var="item" varStatus="status">
                    <div>
                        <p:inputText value="#{item.key}" />
                        <p:inputText value="#{item.value}" />

                        <p:commandButton 
                            value="Delete" 
                            actionListener="#{exampleBean.list.remove(item)}" 
                            process="@this" 
                            update="example-form:example-container" 
                            rendered="#{!status.first}" />

                        <p:commandButton 
                            value="Add" 
                            actionListener="#{exampleBean.addNewItem()}" 
                            process="@this" 
                            update="example-form:example-container" 
                            rendered="#{status.first}" />
                    </div>

                </ui:repeat>
            </div>
        </h:form>
    </h:body>
</html>

With the rendered attribute of each of my p:commandButton, I want to display the add button only on the first item, and the delete button on all items except the first one (making it undeletable).

My problem here, is that using rendered="#{status.first}" on the adding button make the whole thing not working.

<p:commandButton ... rendered="#{status.last}" /> <!-- actionListener called -->

<p:commandButton ... rendered="#{true}" /> <!-- actionListener called -->

<p:commandButton ... rendered="#{status.first}" /> <!-- actionListener NOT called -->

With rendered="#{status.first}", a button click do not call actionListener but trigger the update.

I do not have any idea what could change between displaying it in the first item rather than in others or last one.

robinvrd
  • 1,760
  • 12
  • 28
  • 1
    So if you don't use the 'rendered' attribute all buttons work? Even if you start with e.g. 10 records in the list? Since you 'violate' #1 and #8 from https://stackoverflow.com/questions/2118656/commandbutton-commandlink-ajax-action-listener-method-not-invoked-or-input-value and you might violate #6 depending on the right or wrong sessionscope. – Kukeltje Feb 19 '19 at 19:03
  • @Kukeltje I wanted to make the example simple and I forgot to put the `` tag and the `` tag is in the template. Sorry for this, I updated it. – robinvrd Feb 20 '19 at 08:19
  • Code should be a [mcve]... Try without a template/composition and post the `h:head` in this file. There are reasons for using creating a [mcve]: Help you narrow down the problem (or mybe even solve it) and provide code for us to have a clear and explicit basis for helping out. Also see https://stackoverflow.com/tags/jsf/info – Kukeltje Feb 20 '19 at 11:04
  • @Kukeltje Sorry for this, I reproduced my error starting from scratch and updated the files code. I also added my version and implementation of JSF. – robinvrd Feb 20 '19 at 11:29
  • Ok, I'll try later today to see if I can explain things. Thanks for making it a real [mcve]. Very helpful – Kukeltje Feb 20 '19 at 11:42
  • @Kukeltje Did you find time to try it ? – robinvrd Feb 21 '19 at 08:09
  • Sorry, no, due to illness I have not touched my laptop. Will try over the weekend – Kukeltje Feb 22 '19 at 09:00

1 Answers1

0

I think this is releated to the <ui:repeat> flag and the generation of unique ID's for the command buttons. The view is probably gets confused. The generated ID's change when you add additional buttons. Plus, introducing the rendered attribute also changes things in the way the component is processed.

So to solve this, I think you need to reprocess the ui:repeat component and the command buttons in order to get valid action URL's. The solution is simple - remove process="@this".

I tweaked your example with a working solution (the example uses Lombok);

@Data @Named @ViewScoped
public class ExampleBean implements Serializable {

    private List<IntegerPair> list;

    @PostConstruct
    private void init() {
        list = new ArrayList<>();
        addNewItem();
    }

    public void addNewItem() {
        list.add(new IntegerPair(1, 300));
    }

    @Data
    @AllArgsConstructor
    public class IntegerPair {
        private int key, value;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:ui="http://java.sun.com/jsf/facelets"  xmlns:p="http://primefaces.org/ui" 
      xmlns:jsf="http://xmlns.jcp.org/jsf">

    <h:head>
        <title>Example</title>
        <meta charset="utf-8" />
    </h:head>

    <h:body>
        Test!
        <h:form id="example-form">
            <div jsf:id="example-container">
                <ui:repeat value="#{exampleBean.list}" var="item" varStatus="status">
                    <div>
                        <p:inputText value="#{item.key}" />
                        <p:inputText value="#{item.value}" />

                        <p:commandButton 
                            value="Delete" 
                            actionListener="#{exampleBean.list.remove(item)}" 
                            update="example-form:example-container" 
                            rendered="#{!status.first}" />

                        <p:commandButton 
                            value="Add" 
                            actionListener="#{exampleBean.addNewItem}" 
                            update="example-form:example-container" 
                            rendered="#{status.first}" />
                    </div>

                </ui:repeat>
            </div>
        </h:form>
    </h:body>
</html>

Notice the IntegerPair class? I had to introduce this because the Pair<> class defined in JavaFX does not have a writable key property - writeable properties are required by JSF when it processes the backing bean value of input components.

Frankly, as the problem you are facing is originating from the <ui:repeat> component/tag, doing process="example-form:example-container" on your command buttons should also work just fine - just make sure <ui:repeat> is included in the processing stage.

Adam Waldenberg
  • 2,271
  • 9
  • 26
  • Thank you Adam ! 2 answers in a row, you saved me ! I did not know Lombok btw, is it well known/used in JEE development ? It looks really easy and so much clearer to build classes with it. – robinvrd Feb 27 '19 at 11:15
  • Glad to help @robinvrd! Yes Lombok is popular, not only in JEE development but in many Java projects in general, as it cuts down on boilerplate code, adds some great functionality and cleans up classes very nicely. Personally, I always use it in new Java projects. – Adam Waldenberg Feb 27 '19 at 16:54