Jsf ui:repeat size doesn't gets java bean value
Asked Answered
P

1

1

JSF 2.0's ui:repeat tag gets the value of java bean(arraylist) as it's value property but size property doesn't. I am using the ui repeat inside of a datatable which shows statuses iteratively and ui repeat shows comments for each status. I am giving the size property of ui repeat from a java class because each status has a different number of comments. Therefore size should be decided dynamically. Here is the summary of what i've done. Model classes:

@ManagedBean
@RequestScoped
public class Comment {
     private String commentAuthorName;
//getter and setter
}

This represents the Status class which has a list of comments:

@ManagedBean
@RequestScoped
public class Status {
    private ArrayList<Comment> commentList;
    private int numOfComments;
//getter and setter
}

This is giving an idea about StatusBean class:

@ManagedBean
@SessionScoped
public class StatusBean {
    List<Status> panelList = new ArrayList<Status>();
    List<Comment> commentList = new ArrayList<Comment>();
    public static void process() {
        panelList = StatusService.getPersonalStatus(log.getLoggeduser());//means fill list
        commentList = StatusService.getPersonalComments(panelList);//gets comments via related statuses
        for (int i=0; i<panelList.size(); i++) { //for each status
             Status status = panelList.get(i);
             for(Comment comment : commentList) { //for each comment of each status
             status.setNumOfCommentsShown(1);
           }
        }
    }
}

And view layer is sth like below. Ui repeat included in PrimeFaces DataTable to be able to show each comment for each status. I am using datatable because it has live scroll and it has to show all statuses iteratively and ui repeat looks best to show each comment for each status.

<p:dataTable liveScroll="true" value="#{StatusBean.panelList}" 
             var="Status" scrollable="true">
   <ui:repeat var="Comment" value="#{Status.commentList}" 
           size="#{Status.numOfComments}"></ui:repeat>
</p:dataTable>

Debug result shows the #{Status.numOfComments} is correctly filled with expected integer but still it's not working. But if i write size=3 manually, it gives the expected result.

Philharmonic answered 12/2, 2013 at 13:43 Comment(13)
If you would like to get concrete help please do post a sscce of your code, otherwise no one will be able, or want, to answer your question. As a side note, your code shows some disrespect for Java Naming Conventions which might make some readers repulsed from answering.Discontented
Thanks for your useful comment for a trainee. I updated my question due to your suggestions btw i don't feel OK today, sorry for mistakes.Hooray
<ui:repeat var="Comment" value="#{Status.commentList}" size="#{Status.numOfComments}"</ui:repeat> is not valid xhtml, you are missing a >.Alta
Thanks Christophe i am not copying my original code because of it's complexity. This code has typed for this question it can include few syntax errors as i said. Those are not related with the original problem.Hooray
I don't undestand what you want to achieve with an inner <ui:repeat> inside <p:dataTable>. You have other PrimeFaces components that could help you like <p:subTable> and <p:rowExpansion>.Aeolipile
There are huge problems with the design of your model. If I were you I would have rearranged everything more logically. The structure is really hard to follow in the way it is proposed here. I think that you need one bean and everything else inside it should be a simple database-derived model.Discontented
Luiggi thanks for informing about options especially subtable does same thing but why ui repeat size not getting bean value question is not answered. Skuntsel my model is even more complex than this. There is already a dao class which makes its job among database. Believe me putting comments and status in a one class will make it even harder to follow.Hooray
What I meant is that each status element already contains list of comments and that they are entity classes or DTOs rather than managed beans (with request scopes). Next, static method of session scoped bean is a really bad idea. It would be good instead to fill the values either in post construct method, or in page action method. Also, iteration of the second lists is redundant, because there are no method calls on comment object.Discontented
You are right about static methods, i've a plan about re-factoring it later, i am just focusing on the functionality right now. There are method calls on comment object but i didn't included those code areas becuase it's irrelevant to problem's itself.Hooray
Anyway <p:subTable> doesn't provide "Client Side Row Number Limitation" feature as well. I need this feature because each status has different number of comments and ui repeat size property is exactly what i need except considering that it's not workingHooray
And why not consider prefetching the necessary number of database rows in your service getter? Getting back to your problem, you may see that setting the size attribute of <ui:repeat> works when el expression evaluates to an integer value with a quite simple example. Remove (improper use of) class annotations of your Status and Comment and see if it would yield the expected result. If it wouldn't - replace it with <c:forEach> and try it out then.Discontented
I didn't consider prefetching necessary number of rows because i want to decrease server side process intensity but my move has failed so prefetching is the only option. c:forEach doesn't work in p:datatable and i don't know the reason again. ui repeat; what a heartbreake!Hooray
Look at the answer and enjoy JSF 'hidden' features!Discontented
D
5

As far as I see there are no answer up till today, so I'll answer your question and give you some ideas on what I would have definitely changed in your stack.

Analysis of your problem

I have written a code similar to yours regarding the usage of <ui:repeat> with size attribute specified by a managed bean property and it didn't work for me either. No matter how hard I tried to set attribute's value by EL it didn't work. Moreover, it didn't work for the simplest EL like #{5} as well. I don't know where the problem stems from, but I think that the experiences ones here will enlighten us of why it is happening, will you?

Probably it is a glitch of the JSF <ui:repeat> component, then we shall give rise to an issue regarding it. If it is a feature it would be nice to understand it fully.

A working example of your code, as I understand it

Regarding the above code, there are many simple workarounds. In case you are so insistent on using the <ui:repeat> component I'll provide you with the basic working example. Your view layer is backed by a JSF managed bean and two model classes. My solution uses <ui:param> and <ui:fragment>. Here we go.

The view:

<p:dataTable value="#{statusBean.statusesList}" var="status">
    <p:column headerText="Status name">
        <h:outputText value="#{status.statusName}"/>
    </p:column>
    <p:column headerText="Status comments">
        <ul>
            <ui:param name="max" value="#{status.numOfComments}"/>
            <ui:repeat var="comment" value="#{status.commentList}" varStatus="statusVar">
                <ui:fragment rendered="#{statusVar.index lt max}">
                    <li>
                        <h:outputText value="Author: #{comment.authorName}; comment: #{comment.description}"/>
                    </li>
                </ui:fragment>
            </ui:repeat>
        </ul>
    </p:column>
</p:dataTable>

The model:

public class Comment {

    private String authorName;
    private String description;

}

with

public class Status {

    private List<Comment> commentList;
    private int numOfComments;
    private String statusName;

}

The managed bean:

@ManagedBean
@RequestScoped
public class StatusBean {

    private List<Status> statusesList;

    public StatusBean() {
        Status status;
        List<Status> statusesList = new ArrayList<Status>();
        Comment comment;
        List<Comment> commentList;
        for(int s = 0; s < 10; s++) {
            commentList = new ArrayList<Comment>();
            for(int c = 0; c < 20; c++) {
                commentList.add(new Comment("User " + (s + 1) + "-" + (c + 1), "Description for comment " + (s + 1) + "-" + (c + 1)));
            }
            statusesList.add(new Status(commentList, (s + 1), "Status " + (s + 1)));
        }
        this.statusesList = statusesList;
    }

}

Things I would definitely change in your code

After the code is working I would like to state some improvements I'd make.

  1. Make your model a model: get rid of @ManagedBean and @...Scoped annotations and make them (detached) @Entity classes instead, delvered by your service bean;
  2. Try to make the model as simple as possible, retaining the functionality of your application. My example uses only a list of statuses as data holders in your bean: you do not need a duplicate list of comments as it is already there for you in a Status object;
  3. Do NOT use static methods inside beans, rather (pre)load nesessary data in your page action method (Servlet 3.0), or on preRenderView listener (Servlet 2.5), or in a @PostConstruct method. Make a good choice of bean scopes. Consulting an excellent overview by BalusC would be a great starting place;
  4. Preload only the data you need from your data source with a service method call: if you are willing to show a limited amount of comments why fetch them from your datasource? Limit the result sets depending on what you need (remember, if you would like to implement 'view all' feature you may update your elements with ajax and load the rest). Making it work this way eliminates the need for usage of size="...";
  5. Use standard UI components or the components of the component library of your choice, which is Primefaces, so try to follow Luiggi Mendoza's suggestions.

The sample kick-off example of a managed bean follows:

@ManagedBean
@RequestScoped
public class StatusBean {

    private List<Status> statusesList;

    @EJB
    private StatusService statusService;

    @ManagedProperty(value="#{user}")
    private User user;

    @PostConstruct
    public void init() {
        statusesList = statusService.getStatuses(user);
    }

    public List<Status> getStatusesList() {
        return statusesList;
    }

}
Discontented answered 13/2, 2013 at 13:5 Comment(3)
WOW! This must be the best answer that can be given to this question!!! I just implemented your code and works exactly in manner of I want. I am going to try to consider your recommandations of course. When I finish the implementation, I am going to share experiences. Wow i'm so appreciated for this classy and great answer.Hooray
I have a problem which relates your suggestion hereHooray
Hello I Implemented this solution in a ace:dataTable but the ui:repeat not work. You can see my code here : #26761721Vinitavinn

© 2022 - 2024 — McMap. All rights reserved.