0

I've a dataTable within a dataTable. The initial dataTable has a list of components that appear on the page. One of these components will be a list of strings which can have elements added or deleted. When I change a string in this list, I expect the value to show up in the bean and it is not.

Below I have an example of my problem. The page renders a text input field for the first component then three text input fields to represent the second component which is a list of three input fields.

I have valueChange listener on all the input fields. The listener, is in the InnerBean class, prints out the source and the value that changed.
For the standalone input field, the listener correctly prints out the changed value and shows that the bean has been updated with this value. For any of the input fields from the list, the listener prints out the previous value of the input field and the bean has not been updated. On the ajax update of the inner datatable, the changed value is replace with the original value.

Since the valueChange listener is called, it appears that Primefaces knows that the value has changed. The code just doesn't seem to record the changed value.

Any help is appreciated.

I'm using Primefaces 8.0 and JSF 2.2.20.

Here is the xhtml:

                <p:panel id="testPanel" header="#{myController.outerBean.name}" toggleable="true" collapsed="false" >
                    
                    <p:dataTable id="testTable" value="#{myController.outerBean.innerBeanList}" var="bean">
                        
                        <p:column >
                        
                            <!-- TEXT COMPONENT-->
                            <h:panelGroup rendered="#{bean.type eq 'text'}" >
                                <p:inputText id="textfield" value="#{bean.value}" style="width:100%;" >
                                    <p:ajax event="valueChange" listener="#{bean.textListListener}"  update="testTable" />
                                </p:inputText>
                            </h:panelGroup>
                            
                            <!-- LIST COMPONENT -->
                            <h:panelGroup rendered="#{bean.type eq 'textlist'}" >
                                <p:dataTable id="testListTable" styleClass="datatableWithoutBorder" style="width:320px" 
                                        var="textAddition" value="#{bean.list}" rowIndexVar="rowIndex" >    
                                    <p:column >
                                        <p:inputText id="textAdd" value="#{textAddition}" style="width: 100%;">
                                            <p:ajax event="valueChange" listener="#{bean.textListListener}" update="testListTable"/>                                            
                                        </p:inputText>
                                    </p:column>
                                </p:dataTable>
                            </h:panelGroup>
                            
                        </p:column>
                    </p:dataTable>

                    <h:panelGrid columns="1"  style="width:100%;">
                        <h:panelGroup style="float:right">
                            <p:commandButton id="submitBtn" value="Submit"
                                action="#{dummyController.submit}"
                                update="messages @this" 
                                icon="fa fa-save"/>
                        </h:panelGroup>
                    </h:panelGrid>
                </p:panel>

My controller code:

public class MyController {

private OuterBean outerBean;

public MyController() {
    System.out.println("MyController instantiated");
    setOuterBean(new OuterBean());
}

public void submit() {
    for (InnerBean ab: outerBean.getInnerBeanList()) {
        System.out.println(ab.getLabel() + ": " + ab.getValue() + ":" + ab.getList() );
    }
}

public void clear() {
    // TODO
}

// Getter/Setter methods
public OuterBean getOuterBean() {
    return outerBean;
}
public void setOuterBean(OuterBean outerBean) {
    this.outerBean = outerBean;
}

}

My OuterBean with the list of components:

public class OuterBean implements Serializable {

private String name;
private String value;
private List<InnerBean> innerBeanList;

public OuterBean() {

    name = "Entry Panel #1";
    value = "";

    innerBeanList = new ArrayList<InnerBean>();
    InnerBean ab1 = new InnerBean(); 
    ab1.setLabel("First Component");
    ab1.setType("text");
    ab1.setValue("Input text");
    innerBeanList.add(ab1);
    
    InnerBean ab2 = new InnerBean();
    ab2.setLabel("Second Component");
    ab2.setType("textlist");
    ArrayList<String> list = new ArrayList<String>();
    list.add("Item 1");
    list.add("Item 2");
    list.add("Item 3");
    ab2.setList(list);
    innerBeanList.add(ab2);
}

//
// Getter/Setters
//
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getValue() {
    return value;
}

public void setValue(String value) {
    this.value = value;
}

public List<InnerBean> getInnerBeanList() {
    return innerBeanList;
}

public void setInnerBeanList(List<InnerBean> innerBeanList) {
    this.innerBeanList = innerBeanList;
}

}

My InnerBean which represents a component to be render. One of which can be a list of strings:

public class InnerBean implements  Serializable {

// Type of component
public static final String TEXT = "text";
public static final String TEXTLIST = "textlist";

private String label;
private String type;  // If TEXT, use value; if TEXTLIST, use list.
private String value;
private List<String> list = new ArrayList<String>();

public InnerBean() {
}

public void textListListener(AjaxBehaviorEvent event) {
    System.out.println("Listener called");
    System.out.println("       Source: " + event.getSource().toString());
    System.out.println("        Value: " + ((UIInput)event.getSource()).getValue());
    System.out.println("         List: " + list.toString());
    System.out.println("        Event: " + event.toString());

}

//
// Setters and getters
//

public String getLabel() {
    return label;
}

public void setLabel(String label) {
    this.label = label;
}

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}

public String getValue() {
    return value;
}

public void setValue(String value) {
    this.value = value;
}

public List<String> getList() {
    return list;
}

public void setList(List<String> list) {
    this.list = list;
}

}

Amit Kumar Lal
  • 5,537
  • 3
  • 19
  • 37
CQHondo
  • 1
  • 2
  • What is the scope of the backing bean ? Is it `@Dependant` or `@RequestScoped` ? Then the value will only be valid between requests. Also, don't access backing bean values via the controller - use `@Named` and access them directly via the view. – Adam Waldenberg Sep 01 '20 at 09:17
  • Have a look at https://stackoverflow.com/questions/2118656/commandbutton-commandlink-ajax-action-listener-method-not-invoked-or-input-value – Jasper de Vries Sep 01 '20 at 14:56
  • @AdamWaldenberg The bean is SessionScoped. My controller keeps track of different "InnerBeans" so I'm not sure how I would directly access the bean. But thank you. – CQHondo Sep 02 '20 at 00:11
  • @JasperdeVries Thanks for the link to this great answer. I didn't find anything that directly applies but that doesn't mean the answer is not there--I just didn't understand it all. The section on debugging was interesting. I'll try exploring what is happening under the hood to get an idea of what is going on. Thanks. – CQHondo Sep 02 '20 at 00:16
  • Sounds to me like you need to read up on and cover the basics a little better... `@Named` will expose the bean to the JSF view so that you can access it directly. – Adam Waldenberg Sep 06 '20 at 10:35

0 Answers0