8

I want to process this form (valueChangueListener is not valid in real case).

This is the back bean:

public class TestBean extends PrivateBaseBean implements Serializable {
private List<String> strings;

@PostConstruct
public void init() {
    strings = new ArrayList<String>();
    strings.add("");
    strings.add("");
    strings.add("");
}

public void saveAction(ActionEvent event) {

    StringBuilder textToShowInMessage = new StringBuilder();
    for (String string : strings) {
        textToShowInMessage.append(string);
        textToShowInMessage.append("; ");
    }
    FacesMessage msg = new FacesMessage(super.getBundle().getString(
            textToShowInMessage.toString()), "");

    FacesContext.getCurrentInstance().addMessage(null, msg);
}

getters... setters...

An the view:

....
<h:form>
<ui:repeat var="string" value="#{testBean.strings}">
    <h:inputText value="#{string}" />
    <br />
</ui:repeat>
<p:commandButton value="#{msg.save}"
actionListener="#{testBean.saveAction}" icon="ui-icon-disk"
        update="@form" />
</h:form>
...

When the form is processed in the back bean string list always is blank.

How to process form intput's inside iteration, without any value changue listener?

There are some screenshots: Debug code

Form in browser

The same problem occurs with action or actionListener on

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Eric Garcia
  • 190
  • 1
  • 1
  • 8

2 Answers2

14

Your problem is not connected with PrimeFaces <p:commandButton>'s behaviour, but rather with a scoping problem that is implicilty created when using the <ui:repeat> tag.

First of all, let's depart from your example. Basically, you've got

<ui:repeat value="#{bean.strings}" var="s">
    <h:inputText value="#{s}"/>
</ui:repeat>

with the backing List<String> strings.

The culprit is here: value="#{s}". The exported by <ui:repeat> variable s is visible only within its loop and it is not bound to any managed bean's property, but instead only to a local variable. Put it differently, s is not bound/equal to bean.strings[index] as one would expect and has no knowledge, as we see, where it originated from. So basically, you're off with a unilateral relationship: value from the bean is printed in your input properly, but the reverse is not happening.

The workarounds

Workaround #1: wrapper classes / model objects

The situation can be overcome by using a wrapper object for your class. In case of a string it could be a 'simple mutable string', like below:

public class MString {
    private String string;//getter+setter+constructor
}

In this case the iteration will be working as predicted:

<ui:repeat value="#{bean.mstrings}" var="ms">
    <h:inputText value="#{ms.string}"/>
</ui:repeat>

with the backing List<MString> mstrings.

Note that if you have your model class, like User, and will change its properties within <ui:repeat> the class itself will be effectively a wrapper, so that the properties will be set appropriately.

Workaround #2: chained property access

Another workaround consists of accessing an element of your collection directly from within a <h:inputText> tag. This way, any such property will be set by accessing the bean, then collection, then setting the property at the desired index. Excessively long, but that's how it is. As to the how question, <ui:repeat> provides for an exported current iteration status variable, varStatus, that will be used to access the array/collection in the managed bean.

In this case the iteration will also be working as predicted:

<ui:repeat value="#{bean.strings}" var="s" varStatus="status">
    <h:inputText value="#{bean.strings[status.index]}"/>
</ui:repeat>

with the ordinary backing List<String> strings.

skuntsel
  • 11,624
  • 11
  • 44
  • 67
  • `` defaults to `@form`. It's more likely that bean management annotations are bad or the form is nested. This is impossible to answer based on the information provided so far as the OP didn't post a fullworthy SSCCE. – BalusC May 15 '13 at 02:19
  • @BalusC The problem is not tied with underinformed question. Neither it is connected with nesting forms/improperly used annotations. Analysis showed that the problem is connected with the scoping of `` exported `var`. So, I made an edit and proposed two ways to work around it. As to the PrimeFaces `process` I haven't found any documental approval for default `@form`. Moreover, I found `` documentation regarding `process` misleading, as it doesn't override the command button's default value if we nest two plain tags. Still, simple test confirmed that your findings were right. – skuntsel May 15 '13 at 08:22
  • That's basically answered in the other question I suggested to look at :) – Balázs Németh May 15 '13 at 09:29
  • @BalázsMáriaNémeth Oh, yes, exactly. But be sure that when you bind model classes that basically act as wrappers, that is not necessary, what's exactly stated in the updated answer. – skuntsel May 15 '13 at 09:33
  • The second level of this question is here [link](http://stackoverflow.com/questions/16563693/values-of-hinputtext-inside-ptabview-are-not-processed) – Eric Garcia May 15 '13 at 11:22
  • another workaround that worked for me: ` ` – The Student Jan 10 '17 at 17:11
2

My workaround solution take the value directly from the page:

<ui:repeat id="repeat" value="#{bean.strings}" var="s" varStatus="status">
    <h:inputText id="x" value="#{s.field}"/>
    <h:commandLink style="margin: .5em 0" styleClass="commandLink" actionListener="#{bean.save(status.index)}" value="#{bundle.Send}"/>
</ui:repeat>

The save method:

public void save(String rowid) {
        String jsParam = Util.getJsParam("repeat:" + rowid + ":x");
        System.out.println("jsParam: " + jsParam); //persist...
    }

The getJsParam method:

public static String getJsParam(String paramName) {
    javax.faces.context.FacesContext jsf = javax.faces.context.FacesContext.getCurrentInstance();
    Map<String, String> requestParameterMap = jsf.getExternalContext().getRequestParameterMap();
    String paramValue = requestParameterMap.get(paramName);
    if (paramValue != null) {
        paramValue = paramValue.trim();
        if (paramValue.length() == 0) {
            paramValue = null;
        }
    }
    return paramValue;
}
Eitan Rimon
  • 641
  • 7
  • 13