views:

614

answers:

4

Hi,

I have a listing page that goes to an add page. The add page has a name textbox whose value is bound to a session scoped bean.

The listing page has an add button that goes via an action method to the add page. This action method clears the object that the name textbox is bound to.

I also have a cancel button on the add page, which is bound to an action method that again clears the value that the name textbox is bound to.

If nothing is set to immediate, this all works fine.

However, if I set the cancel button to immediate, if I enter values in the name field, and then click cancel, the action method is fired and clears the object in the backing bean and goes to the listing page. If I then click add, the action method clears the object again (ignore if it's best method or not) and then goes to the add page. I would now expect the add page's name textbox to be empty, but it's not?! Surely, since the add button is not immediate, the values should be re-bound and empty?

Below is the relevant XHTML for the add button on the listing page

<h:commandButton id="addButton"
                 value="Add"
                 action="#{myBean.gotoAdd}"/>

Below is the relevant XHTML for the input box on the add page (myBean is session scoped), followed by that of the cancel button on the add page.:

<h:inputText id="newName"
             value="#{myBean.newObject.name}"
             binding="#{myBean.newNameInput}"
             styleClass="name" />

<h:commandButton id="cancelButton"
                 value="Cancel" immediate="true"
                 action="#{myBean.cancelAdd}"
                 onclick="return confirm('You sure?');"/>
+1  A: 

I almost never use the binding property of tags, except for when I need to identify which item of a list has had an action fired on it, so I am not particularly well-informed about its uses. But I know that without using binding your code would most likely work as you expected, so my expectation is that whatever javax.faces.component.UIxxx object you are binding to isn't getting reset correctly.

Naganalf
Taking the binding out did indeed fix the issue. However, I think I might actually need to bind to the component in some cases, so I am investigating clearing the actual component - setting the value programmatically on the component instance in the backing bean. Thanks :)
jamiebarrow
And on a separate note, how would you show/hide different components based on values that are selected - e.g. a set of select items, which show/hide conditionally based on the values selected. I thought it would be better to do so in the backing bean on a pre-render event, which is why I am using the binding.
jamiebarrow
A: 

If you use immediate="true" then the value will be kept, this is how the parameter works. You should take a look at the following links:

http://wiki.apache.org/myfaces/How_The_Immediate_Attribute_Works

http://wiki.apache.org/myfaces/ClearInputComponents

kpolice
I'm using immediate="true" for the Cancel button only. The Add button is not immediate, and yet it doesn't cause the update in the session bean's values...
jamiebarrow
... well, I believe it is actually calling the action method etc, but not applying the values that are set in the session bean. Could be the binding.
jamiebarrow
A: 

Ok, here's an example that I did from scratch. I have two cancel buttons, one that is immediate, and one that isn't. Example of steps to reproduce:

  • Go to james-list page and click Add
  • The add page displays with empty fields. Enter values for all fields and click Add.
  • The listing page displays and is updated to include the new person. Click Add.
  • The add page displays with empty fields. Enter values for all fields and Click Cancel (Immediate)
  • The listing page displays and is unchanged. Click Add.
  • The add page displays however the fields are not empty as I would expect. Click Cancel.
  • The listing page displays and is unchanged. Click Add.
  • The add page displays and NOW the fields are not empty.

James.java:

package com.jamiebarrow;

import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;

@ManagedBean
@SessionScoped
public class James {

  private UIComponent idComponent;
  private UIComponent firstNameComponent;
  private UIComponent lastNameComponent;

  public UIComponent getIdComponent() {
    return idComponent;
  }

  public void setIdComponent(UIComponent idComponent) {
    this.idComponent = idComponent;
  }

  public UIComponent getFirstNameComponent() {
    return firstNameComponent;
  }

  public void setFirstNameComponent(UIComponent firstNameComponent) {
    this.firstNameComponent = firstNameComponent;
  }

  public UIComponent getLastNameComponent() {
    return lastNameComponent;
  }

  public void setLastNameComponent(UIComponent lastNameComponent) {
    this.lastNameComponent = lastNameComponent;
  }

  private List<Person> personResults;

  private Person person;

  public James() {
    personResults = new ArrayList();
    personResults.add(new PersonBuilder(1, "Bob", "Uncle").build());
    personResults.add(new PersonBuilder(2, "Jack", "Black").build());
  }

  public List<Person> getPersonResults() {
    return personResults;
  }

  public void setPersonResults(List<Person> personResults) {
    this.personResults = personResults;
  }

  public Person getPerson() {
    return person;
  }

  public void setPerson(Person person) {
    this.person = person;
  }

  private void clearPerson() {
    person = new PersonBuilder().build();
  }

  public String gotoList() {
    return "james-list";
  }

  public String gotoAdd() {
    clearPerson();
    return "james-add";
  }

  public String cancelAdd() {
    clearPerson();
    return gotoList();
  }

  public String addPerson() {
    personResults.add(person);
    return gotoList();
  }
}

james-list.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"&gt;
<h:head>
  <title>list page</title>
</h:head>

<body>
<div class="container">
  <div class="content">
    <h:messages showSummary="true" showDetail="false" errorClass="error" infoClass="info"
                warnClass="warn"/>
    <h:form>
      <h:dataTable value="#{james.personResults}" var="person">
        <h:column>
          <f:facet name="header">Id</f:facet>
          <h:outputText value="#{person.id}"/>
        </h:column>
        <h:column>
          <f:facet name="header">Name</f:facet>
          <h:outputText value="#{person.firstName}"/>
        </h:column>
        <h:column>
          <f:facet name="header">Surname</f:facet>
          <h:outputText value="#{person.lastName}"/>
        </h:column>
      </h:dataTable>
      <h:panelGroup layout="block">
        <h:commandButton value="Add" action="#{james.gotoAdd}"/>
      </h:panelGroup>
    </h:form>
  </div>
</div>
<ui:debug hotkey="L" rendered="true"/>
</body>
</html>

james-add.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"&gt;
<h:head>
  <title>add page</title>
</h:head>

<body>
<div class="container">
  <div class="content">
    <h:messages showSummary="true" showDetail="false" errorClass="error" infoClass="info"
                warnClass="warn"/>
    <h:form>
      <fieldset>
        <legend>Add Person</legend>
        <h:panelGrid columns="2">
          <h:outputLabel for="PersonId" value="Id:"/>
          <h:inputText id="PersonId" value="#{james.person.id}" binding="#{james.idComponent}"/>
          <h:outputLabel for="PersonFirstName" value="First Name:"/>
          <h:inputText id="PersonFirstName" value="#{james.person.firstName}" binding="#{james.firstNameComponent}"/>
          <h:outputLabel for="PersonLastName" value="Last Name:"/>
          <h:inputText id="PersonLastName" value="#{james.person.lastName}" binding="#{james.lastNameComponent}"/>
        </h:panelGrid>
        <h:panelGroup layout="block">
          <h:commandButton value="Add" action="#{james.addPerson}"/>
          <h:commandButton value="Cancel (immediate)" action="#{james.cancelAdd}" immediate="true"/>
          <h:commandButton value="Cancel" action="#{james.cancelAdd}"/>
        </h:panelGroup>
      </fieldset>
    </h:form>
  </div>
</div>
<ui:debug hotkey="L" rendered="true"/>
</body>
</html>
jamiebarrow
Removing the binding attributes fixes this problem. But what if I want to use the binding, how do I ensure that the fields are cleared?
jamiebarrow
I just poked around the API docs and it looks like the UIInput component has a setValue method, try that.
Naganalf
Tried using it with no luck if I recall. Even tried changing the value binding programmatically using the EL methods but also no luck. Removing the binding, or making immediate="false" seems to be the only solution... which is weird IMHO.
jamiebarrow
+1  A: 

I'm having very similar problems right now.

Besides removing the binding and/or immediate attribute, try calling setSubmittedValue() on component with binding from action called upon click on 'add' button.

Alas, even if it helps you, you would still have to do it in any action that can lead to displaying same component after cancel.

That's why I'm still trying to figure out some better solution...

Marko Mitrovic