0

I have been trying to find out what is the problem for quite long time and no successful results for me. I was searching for this in Google and I tried out few different working examples but none helped me to solve my task.

The problem is that "Messages" selectOneMenu just does not update when "Categories" selectOneMenu is changed. It does not load the messages of selected category.

In HTML I have just two selectOneMenu elements. In service class I make Users, Categories and Messages and add them to linking lists.

HTML

    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core" 
      xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    </h:head>

    <h:body>
        <h:form>
            <h:outputText value="You have no categories made yet..." rendered="#{empty categoryBean.categories}" />
            <h:selectOneMenu value="#{categoryBean.category}" rendered="#{not empty categoryBean.categories}">
                <f:selectItems value="#{categoryBean.categories}" />
                <f:ajax event="change" render="messages" listener="#{categoryBean.changeCategory}" />
            </h:selectOneMenu>

            <h:outputText value="This category has no messages yet..." rendered="#{empty categoryBean.messages}" />
            <h:selectOneMenu value="#{categoryBean.message}" id="messages" rendered="#{not empty categoryBean.messages}">
                <f:selectItems value="#{categoryBean.messages}" />
            </h:selectOneMenu>
        </h:form>
    </h:body>
</html>

Bean:

    package beans;

    @Named(value = "categoryBean")
    @SessionScoped
    public class CategoryBean implements Serializable {
        private List<Message> messages;
        private Category category;
        private Message message;

        @Inject
        private Service service;
        /**
         * Creates a new instance of CategoryBean
         */
        public CategoryBean() {
            category = new Category();
        }

        public void setCategory(Category category) {
            this.category = category;
        }

        public Category getCategory(){
            return this.category;
        }

        public List<Category> getCategories() {
            return service.getCategories();
        }

        public List<Message> getMessages() {
            return this.category.getMessageList();
        }

        public Message getMessage() {
            return message;
        }

        public void setMessage(Message message) {
            this.message = message;
        }

        public void changeCategory() {
            this.messages = category.getMessageList();
        }
    }

Service:

package service;

@Named(value = "service")
@ApplicationScoped
public class Service {
    private final ArrayList<User> users = new ArrayList<>();

    public Service() {
        User u1 = new User("Mikolaj", "Stasinski", "mikolaj", "123456", 2);
        User u2 = new User("Toms", "Bugna", "toms", "789012", 2);
        User u3 = new User("Algie", "Tiempo", "algiemay", "123456", 1);

        users.add(u1);
        users.add(u2);
        users.add(u3);

        Category c1 = new Category(u1, "SADP", "Advanced Java");
        Category c2 = new Category(u1, "SADP Kaj", "JSF");
        Category c3 = new Category(u2, "CNDS", "Networking");

        u1.addCategory(c1);
        u1.addCategory(c2);
        u2.addCategory(c3);

        Message m1 = new Message(u3, c3, "This is a message.");
        Message m2 = new Message(u3, c1, "Another Message1");
        Message m3 = new Message(u3, c1, "Another Message2");

        u3.addMessage(m1);
        u3.addMessage(m2);
        u3.addMessage(m3);

        c1.addMessage(m2);
        c1.addMessage(m3);
        c3.addMessage(m1);
        c2.addMessage(m3);
    }

    public List<User> getUsers(){
        return new ArrayList<>(users);
    }

    public List<Category> getCategories(){
        ArrayList<Category> categoryList = new ArrayList<>();

        for (User u: users) {
            for (Category cat: u.getCategoryList()) {
                categoryList.add(cat);
            }
        }

        return categoryList;
    }
}
RobertB
  • 4,592
  • 1
  • 30
  • 29
Toms Bugna
  • 502
  • 2
  • 10
  • 30

2 Answers2

1

It might be because the signature of you listener method is not correct, it should look like this:

public void changeCategory(AjaxBehaviorEvent event) {     
   this.messages = category.getMessageList();   
}    

Have a look at this post The f:ajax listener method in h:selectOneMenu is not executed and follow the working example.

Community
  • 1
  • 1
Mooolo
  • 428
  • 2
  • 7
  • I have tried this and just tried one more time. Nope. It does not work. Still not changing the second SelectOneMenu. Any ideas? – Toms Bugna Nov 21 '14 at 18:09
  • The problem is that on first SelectOneMenu change the categoryBean.category variable does not change to selected category. How I can fix this? – Toms Bugna Nov 21 '14 at 18:26
1

The problem is the rendered condition is in the same place as the id. When a component isn't rendered, its id can't be found. Then, the first select can't refresh the second select correctly. Insert the components with the rendered condition within a container with the id used to refresh:

<h:form>
    <h:outputText 
        value="You have no categories made yet..." 
        rendered="#{empty categoryBean.categories}" />

    <h:selectOneMenu 
        value="#{categoryBean.category}" 
        rendered="#{not empty categoryBean.categories}">
        <f:selectItems value="#{categoryBean.categories}" />
        <f:ajax event="change" 
            render="messages" 
            listener="#{categoryBean.changeCategory}" />
    </h:selectOneMenu>

    <h:panelGroup id="messages">
        <h:outputText 
            value="This category has no messages yet..." 
            rendered="#{empty categoryBean.messages}" />
        <h:selectOneMenu 
            value="#{categoryBean.message}" 
            rendered="#{not empty categoryBean.messages}">
            <f:selectItems value="#{categoryBean.messages}" />
        </h:selectOneMenu>
    </h:panelGroup>

</h:form>

Update: The second problem is your selects are managing objects. Internally the selectOneMenu use Strings to manage the values, to use objects you need to implement and use a Converter for each type. In this Converter class you need to convert your object in a String, and this String in the same object again. You can see more information here: How create a custom coverter in JSF 2?

Community
  • 1
  • 1
Corus
  • 101
  • 3
  • It flags the error that one "Messages" id has been already found. It is because panelGroup has id "Messages" and selectOneMenu has id "Messages". Which one should I change to some other id name? – Toms Bugna Nov 21 '14 at 19:55
  • Sorry, I forgot to remove the messages id from the selectOneMenu. I upgraded the answer. – Corus Nov 21 '14 at 19:57
  • Still does not work. As I already previously said in answer to user Mooolo, I think that on first SelectOneMenu changing process the "categoryBean.category" variable does not change to selected category/item. – Toms Bugna Nov 21 '14 at 20:02
  • Sorry again, I haven't seen the second problem, I tested with a select of strings, not of objects :P. Answer updated – Corus Nov 21 '14 at 20:11
  • I understand what and why you were suggesting me to do, but I'm afraid that I will not be able generate the code of this converter :/ I just tried to make it but it did not really work.. – Toms Bugna Nov 21 '14 at 20:38