tags:

views:

3370

answers:

2

I have a situation where there is a selectOneMenu that has a value bound to a backing bean.

I need to have a button that doesn't update model values (that is why it has immediate="true" property).

That button's action method changes the value the selectOneMenu is bound to, but when the page is redisplayed the original value is displayed (the one that was submitted) and not the one set in the action method.

Any ideas why that is happening?

If I didn't explain the problem good enough please let me know.


EDIT: As requested here is the source code in question:

page code:

<h:selectOneMenu id="selectedPerson" 
                 binding="#{bindings.selectPersonComponent}" 
                 value="#{bean.selectedPerson}">
   <s:selectItems var="op" value="#{bean.allPersons}" 
                  label="#{op.osoba.ime} #{op.osoba.prezime}" 
                  noSelectionLabel="#{messages.selectAPerson}">
   </s:selectItems>
   <f:converter converterId="unmanagedEntityConverter" />
</h:selectOneMenu>
...
<a4j:commandButton action="#{bean.createNew}" value="#{messages.createNew}"
     immediate="true" reRender="panelImovine">
</a4j:commandButton>

java code:

private Person selectedPerson;

public String createNew() {
    log.debug("New created...");
    selectedPerson = null;
    bindings.getSelectPersonComponent().setSubmittedValue(null); //SOLUTION
    return "";
}

The solution is in the lined marked SOLUTION :)

+1  A: 

As it frequently happens a few moments after posting this question I have found an answer:

The cause of the problem is in detail explained here: ClearInputComponents

The problem is (as explained) that model values haven't been updated so the submitted inputs are still in component.submittedValue field and that field is displayed if not empty. It is emptied normally after model has been updated.

The first solution didn't work in my case because there is other important state in the view that mustn't get lost. But the second solution worked great:

component.setSubmittedValue(null);

And that was all that was needed: it is a little extra work because components must be bound to some bean, but not that bad.

ivans
A: 

To follow up a bit, I don't think you need to bind the component to a bean. You can just grab the component from the FacesContext instance via the UIViewRoot, if you know the component's client ID.

It would go a little something like this:

Foo component = (Foo)FacesContext.getCurrentInstance().getViewRoot().getComponent(clientId);

Where Foo is the class of the component you are using and clientId is the client ID of the component in the "formId:elementId" format that JSF uses.

Evgeny
Thanks for your comment. This would work but in my experience knowing clientId isn't that easy and isn't really safe. For example if you move your component around, nest it, change components id, change id of the form, clientid will change and the code will stop working.
ivans
Yes, agreed. This works best with components that have ids that you can assign yourself via the tag and that aren't part of a deeply nested structure.
Evgeny