2

Working on a school-project when suddenly we found a task that is so repetitive that we decided to make a partialView out of it. It basically is a table compiled out of a list saved in a @Named and @SessionScoped (both CDI) bean.

The code of partialView.xhtml:

<ui:composition>
    <h:dataTable
            /* Style */>
        <c:forEach items="#{playListVM.playList.songs}" var="song">
            <f:facet name="caption">
                #{playListVM.playList.name}
            </f:facet>
            <h:column>
                <f:facet name="header">Index</f:facet>  
                #{playListVM.playList.songs.indexOf(song)+1}
            </h:column>
...

I want to call this "method" multiple times like so:

The code of home.xhtml:

<c:forEach items="#{c.playLists}" var="playList">
    <ui:include src="#{c.playListPV(playList)}" />          
</c:forEach>

playListPV(playlist) sets the value of playListVM.playList to playList and returns the path to the .xhtml file.

First we tried it only with h:dataTable which worked fine as long as we used the partialView once per render (otherwise it used the same reference and every PV was identical). So I googled some more and fond out that I just overwrite the object everytime I invoked the method. Then I read this article about c:forEach and the order the things are rendered. Here we are.

By now I think the whole approach is wrong...

DavidS
  • 5,022
  • 2
  • 28
  • 55
  • On another note, outside of your use of ``, I'm not so sure about your control logic. Won't your `forEach` create more _columns_ for every song? I would have thought you'd want more _rows_. – DavidS Apr 15 '15 at 23:29
  • 1
    You might also want to read [jstl-in-jsf2-facelets-makes-sense](http://stackoverflow.com/questions/3342984/jstl-in-jsf2-facelets-makes-sense) in case you have specific problems using `forEach`. – DavidS Apr 15 '15 at 23:34

3 Answers3

4

I gather that you initially tried like this:

<h:dataTable value="#{c.playLists}" var="playList">
    <ui:include src="#{c.playListPV(playList)}" />          
</h:dataTable>

This indeed won't work the way you expected. The <ui:include> runs as being a taghandler during the view build time while the <h:dataTable> runs as being an UI component during the view render time, which is after the view build time. So, at the moment <ui:include> runs, the #{playList} is not set in the scope. Effectively, you end up with one include whose src equals to c.getPlayListPV(null).

You need to create multiple <ui:include>s during view build time instead. You should be using an iteration taghandler which runs during view build time. JSTL <c:forEach> is indeed such one. To make the code yet cleaner, transform the include file into a tag file.

That said, the term "partial view" has in JSF a completely different meaning than what you thought. This has got nothing to do with <ui:include>. The term "partial view" concerns the part of the view which is being processed during a partial request (i.e. it are exactly those components covered by <f:ajax execute> and <f:ajax render>).

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • could I also use Composite Component? –  Apr 16 '15 at 16:12
  • 1
    Nope. Although you *can*, it serves a different purpose. See also http://stackoverflow.com/questions/6822000/when-to-use-uiinclude-tag-files-composite-components-and-or-custom-componen/ – BalusC Apr 16 '15 at 16:12
1

By now I think the whole approach is wrong...

I think you're right, and your expectation of the ui-include tag is wrong. The ui-include tag can always be replaced by simply copy-pasting the included file into the target file; in other words, all you are doing in your example is appending identical Facelets components in a loop.

There is no special variable scope for an included file. The output is rendered only once, and when it is rendered the final value of playListVM.playList will be used.

I think you can achieve your aims with something like this:

<c:forEach items="#{playListVM.playlists}" var="playlist">
  <c:forEach items="#{playlist.songs}" var="song">

I don't mean this exactly; you would need to adapt it to suit your application.

DavidS
  • 5,022
  • 2
  • 28
  • 55
  • "The `ui-include` tag can always be replaced by simply copy-pasting the included file into the target file." I should mention that I only _think_ this is correct; that is, I couldn't think of a counter-example. – DavidS Apr 15 '15 at 23:23
1

If I understand what you're trying to do correctly, here is a basic working example.

PlayListMB :

@Named
@ViewScoped
public class PlayListMB implements Serializable {

    private static final long serialVersionUID = 1L;

    private List<List<String>> listOfPlayLists;

    public PlayListMB() {
        listOfPlayLists = new ArrayList<List<String>>();
        for (int i = 0; i < 5; i++) {
            List<String> playlist = new ArrayList<String>();
            for (int j = 0; j < 5; j++) {
                playlist.add("song" + String.valueOf(i+1) + String.valueOf(j+1));
            }
            listOfPlayLists.add(playlist);
        }
    }

    public List<List<String>> getListOfPlayLists() {
        return listOfPlayLists;
    }
}

singlelist.xhtml :

<ui:composition>

    <h:dataTable value="#{playList}" var="songName">
        <h:column>
            <f:facet name="header">Index</f:facet>  
            #{playList.indexOf(songName) + 1}
        </h:column>
        <h:column>
            <f:facet name="header">Song Name</f:facet>  
            #{songName}
        </h:column>
    </h:dataTable>

</ui:composition>

index.xhtml :

<h:head>
</h:head>
<h:body>

    <c:forEach items="#{playListMB.listOfPlayLists}" var="playList">
        <ui:include src="singlelist.xhtml"/>
    </c:forEach>

</h:body>
tt_emrah
  • 1,043
  • 1
  • 8
  • 19