jsf-2lifecycleselectmanylistbox

Trouble with lifecycle in JSF 2.0 and selectManyListbox


I am trying to make a simple admin page for managing users list. The jsf code looks like this:

 <h:selectOneMenu  id="selectUser" value="#{adminBean.user_id}" valueChangeListener="#{adminBean.userSelected}" >
                    <f:selectItems value="#{adminBean.myModelUsersValues}" />   
                    <a4j:ajax event="valueChange" render="login password privilege_list" execute="@this"/>      
                 </h:selectOneMenu > 

   <table>
      <tr>
        <td><h:outputLabel styleClass="LabelStyle" value="login: "/></td>         
      </tr>
      <tr>
        <td>
            <h:inputText id="login" value="#{adminBean.login}"/>
        </td>   
        <td>
        <h:message for="login" style="color:red"/>
        </td>                        
      </tr>
      <tr>
        <td><h:outputLabel styleClass="LabelStyle" value="password: "/></td>         
      </tr>
      <tr>
        <td>
            <h:inputText id="password" value="#{adminBean.password}"/>
        </td>   
        <td>
        <h:message for="password" style="color:red"/>
        </td>                      
      </tr>       
      <tr>
        <td><h:outputLabel styleClass="LabelStyle" value="privilege list: "/></td>         
      </tr>
      <tr>
        <td>
            <h:selectManyListbox  id="privilege_list" value="#{adminBean.privilegeList}">
                <f:selectItems value="#{adminBean.privilegeValues}" />                          
             </h:selectManyListbox > 
        </td>    
        <td>
        <h:message for="privilege_list" style="color:red"/>
        </td>                      
      </tr>
    </table>
    <br/>
    <h:commandButton id="addButton" value="Add" action="#{adminBean.addUser}" styleClass="ButtonStyle"/>               
    <h:commandButton id="deleteButton" value="Delete" action="#{adminBean.deleteUser}" styleClass="ButtonStyle"/>                  
    <h:commandButton id="clearButton" value="Clear" action="#{adminBean.clear}" styleClass="ButtonStyle"/>  

The problem is that when the page loads, all the items are empty. Now When I click on 'add' button I have discovered that the valueChangeListener="#{adminBean.userSelected}" runs, which replaces my privilege list with the ones from the first user. The same is when I use the clear button - all fields are empty, but when I click on the add button again, the list is the one from the first user (and only the list - no other input texts). I tried adding immediate="true" to the add button and that solves this problem, but off course then all the values I put into input text are not passed through to the adminBean.addUser action method. My bean is viewscoped (I needed to use it because of the validation error on selectManyListBox). Here is the Java code (the addUser method so far only sends a logger method and checks i login exists, and if sth was selected on the priv. list):

@ManagedBean(name="adminBean")
@ViewScoped
public class AdminBean {
private String user_id ="";
private String login ="";
private String password ="";
private ArrayList<String> privilegeList = new ArrayList<String>();

private User model = new User();
private TreeMap<String, User> usersValuesBackendMap = new TreeMap<String, User>();
private TreeMap<String, String> privilegesValues = new TreeMap<String, String>();
private TreeMap<String, String> myModelUsersValues = new TreeMap<String, String>();

...

    @javax.annotation.PostConstruct
    public void init()
    {       

        usersValuesBackendMap = queryDAO.getAllUsers();

        for (Map.Entry<String, User> usr : usersValuesBackendMap.entrySet()) {
            myModelUsersValues.put(usr.getValue().getLogin(), usr.getKey() );
        }

        privilegesValues = queryDAO.getFullPrivilegeList();
        user_id = ""; 
    }
    public void userSelected(ValueChangeEvent event){
           String newValue = event.getNewValue().toString();       

           User user = usersValuesBackendMap.get(newValue);

           login = user.getLogin();
           password = user.getPassword();
           privilegesValues.clear();
           for (String privilege: user.getPrivilegeValues() ){      
               privilegesValues.put(privilege, privilege);
           }
       }

        public String clear(){

            user_id ="";
            login ="";
            password ="";
            privilegesValues = queryDAO.getFullPrivilegeList(); 
            return "";
        }

Interestingly I added immediate="true" to the clearing method and then sth. opposite happens - the list is OK but the inputTexts are filled.


Solution

  • Some facts:

    1. The valueChangeListener runs when !oldValue.equals(newValue).
    2. The valueChangeListener is not a client side event listener. It's entirely server side.
    3. The valueChangeListener is completely independent on whether it's an ajax or normal request. It's invoked on all types of requests, including the normal form submit.

    You need <a4j:ajax listener> instead.

    <h:selectOneMenu  id="selectUser" value="#{adminBean.user_id}">
        <f:selectItems value="#{adminBean.myModelUsersValues}" />   
        <a4j:ajax event="valueChange" listener="#{adminBean.userSelected}" render="login password privilege_list" execute="@this"/>      
    </h:selectOneMenu > 
    

    with

    public void userSelected(AjaxBehaviorEvent event){
        User user = usersValuesBackendMap.get(user_id);
        // ...
    }