Search code examples
jsfjsf-2scopeuirepeat

Magic ui:repeat var


I have stumbled upon an, at least for me, unexpected behaviour. When using an ui:repeat, it seems I can access the var from outside.

Code - Page:

<f:metadata>
   <f:event type="preRenderView" listener="#{xTest.init()}" />
</f:metadata>

<h:form id="xTestForm">
   <h:panelGroup layout="block">
      Track: #{trk.name}
   </h:panelGroup>

   <table>
      <ui:repeat
         value="#{xTest.trackList}"
         var="trk">
         <tr>
            <td>#{trk.name}</td>
            <td>
               <p:commandLink
                  actionListener="#{xTest.setTrack(track)}"
                  value="test"
                  update=":xTestForm" />
            </td>
         </tr>
      </ui:repeat>
   </table>
</h:form>

Code - Bean

package beans;

import dao.DAOFactory;
import dao.track.TrackDAO;
import dto.Track;
import exceptions.DAOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import util.MessageUtil;

@ManagedBean
@ViewScoped
public class xTest implements Serializable {

    private DAOFactory daoFactory = Config.getInstance().getDAOFactory();
    private TrackDAO trackDAO;
    private Track track = new Track();
    private MessageUtil msg = new MessageUtil();
    private List<Track> trackList = new ArrayList();

    public xTest() {
        trackDAO = daoFactory.getTrackDAO(true);
    }

    public void init() {
        if (!FacesContext.getCurrentInstance().isPostback()) {
            try {
                trackList = trackDAO.listByAlbumid(241);
            } catch (SQLException | DAOException ex) {
                msg.setErrorMessage(ex);
            }
        }
    }

    public List<Track> getTrackList() {
        return trackList;
    }

    public void setTrack(Track track) {
        this.track = track;
    }
}

If I click a link in the list of tracks, the track name will be displayed in the panelGroup. How is this possible?


Solution

  • This is a bug in Mojarra. Its UIRepeat component forgets to remove the iteration variable from the request scope by end of iteration during restore view phase. It doesn't work that way in for example MyFaces.

    You shouldn't rely your business code on it. Note that <h:dataTable> doesn't have this problem, it properly removes the iteration variable from the request scope by end of iteration by ((UIData) component).setRowIndex(-1) in encodeEnd() method.