ICEfaces
  1. ICEfaces
  2. ICE-8098

ace:dataTable - f:attribute and f:setPropertyActionListener not working for filtered data

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: EE-3.0.0.GA
    • Fix Version/s: 3.1.0.BETA2, 3.1
    • Component/s: ACE-Components
    • Labels:
      None
    • Environment:
      All
    • Assignee Priority:
      P2

      Description

      Issue scenario:

      An ace:dataTable is set to be filterable. This table includes a column that has a commandLink that uses an f:attribute or f:setPropertyActionListener so that row level information can be passed in. This works fine on the initial rendering of the table. Once a column is filtered and the order of rows is changed the value retrieved from the f:attribute or f:setPropertyActionListener is not the one that corresponds with the visible row.

        Activity

        Hide
        Arran Mccullough added a comment -

        Attached test case that shows the issue with f:setPropertyActionListener. The f:attribute implementation is commented out.

        Steps to reproduce:

        • Load welcomeICEfaces.jsf
        • Click on the view link to show the row info.
        • In the first columns filter input enter in a 'p'.
        • The data is dfiltered to two rows. Clicking on the view links will show the row info for the previous data set.
        Show
        Arran Mccullough added a comment - Attached test case that shows the issue with f:setPropertyActionListener. The f:attribute implementation is commented out. Steps to reproduce: Load welcomeICEfaces.jsf Click on the view link to show the row info. In the first columns filter input enter in a 'p'. The data is dfiltered to two rows. Clicking on the view links will show the row info for the previous data set.
        Hide
        Nils Lundquist added a comment - - edited

        My suspicion is that this is due to the data model being generated (via getDataModel()) with a list that is not the filtered list(via getValue()), even though we have a filtered list in our state we should be exposing.

        This is usually due to our state saving breaking because the table clientId contains iterative indexes. When getFilteredData() is called is called in getValue(), null is returned (because the state doesn't exist for the incorrect clientId) and the UIData implementation of getValue() supplies the backing for the data model.

        Up until now, our solution for this issue has been putting off fixing it at large and simply avoiding it and not calling methods that access the component state (as held by the meta class generated properties) during an iterative context. However this appears to be a framework initiated instance where we need that state during iteration, and I'm not sure how we're to work around it.

        Show
        Nils Lundquist added a comment - - edited My suspicion is that this is due to the data model being generated (via getDataModel()) with a list that is not the filtered list(via getValue()), even though we have a filtered list in our state we should be exposing. This is usually due to our state saving breaking because the table clientId contains iterative indexes. When getFilteredData() is called is called in getValue(), null is returned (because the state doesn't exist for the incorrect clientId) and the UIData implementation of getValue() supplies the backing for the data model. Up until now, our solution for this issue has been putting off fixing it at large and simply avoiding it and not calling methods that access the component state (as held by the meta class generated properties) during an iterative context. However this appears to be a framework initiated instance where we need that state during iteration, and I'm not sure how we're to work around it.
        Hide
        Ted Goddard added a comment - - edited

        The implementation of f:setPropertyActionListener is very simple: for this page we have

        <ace:dataTable value="#

        {bean.data}

        " var="entity">

        and setPropertyActionListener evaluates #

        {entity}

        at

        at com.sun.faces.facelets.tag.jsf.core.SetPropertyActionListenerHandler$SetPropertyListener.processAction(SetPropertyActionListenerHandler.java:209)
        at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
        at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:769)
        at javax.faces.component.UICommand.broadcast(UICommand.java:300)
        at javax.faces.component.UIData.broadcast(UIData.java:1093)
        at org.icefaces.ace.component.datatable.DataTable.broadcast(DataTable.java:222)
        at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
        at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
        at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)

        Show
        Ted Goddard added a comment - - edited The implementation of f:setPropertyActionListener is very simple: for this page we have <ace:dataTable value="# {bean.data} " var="entity"> and setPropertyActionListener evaluates # {entity} at at com.sun.faces.facelets.tag.jsf.core.SetPropertyActionListenerHandler$SetPropertyListener.processAction(SetPropertyActionListenerHandler.java:209) at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88) at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:769) at javax.faces.component.UICommand.broadcast(UICommand.java:300) at javax.faces.component.UIData.broadcast(UIData.java:1093) at org.icefaces.ace.component.datatable.DataTable.broadcast(DataTable.java:222) at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794) at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259) at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
        Hide
        Ted Goddard added a comment -

        The relevant code in UIData is:

        FacesContext context = FacesContext.getCurrentInstance();
        // Set up the correct context and fire our wrapped event
        WrapperEvent revent = (WrapperEvent) event;
        if (isNestedWithinUIData())

        { setDataModel(null); }

        int oldRowIndex = getRowIndex();
        setRowIndex(revent.getRowIndex());
        FacesEvent rowEvent = revent.getFacesEvent();
        UIComponent source = rowEvent.getComponent();
        UIComponent compositeParent = null;
        try {
        if (!UIComponent.isCompositeComponent(source))

        { compositeParent = UIComponent.getCompositeComponentParent(source); }

        if (compositeParent != null)

        { compositeParent.pushComponentToEL(context, null); }

        source.pushComponentToEL(context, null);
        source.broadcast(rowEvent);

        Show
        Ted Goddard added a comment - The relevant code in UIData is: FacesContext context = FacesContext.getCurrentInstance(); // Set up the correct context and fire our wrapped event WrapperEvent revent = (WrapperEvent) event; if (isNestedWithinUIData()) { setDataModel(null); } int oldRowIndex = getRowIndex(); setRowIndex(revent.getRowIndex()); FacesEvent rowEvent = revent.getFacesEvent(); UIComponent source = rowEvent.getComponent(); UIComponent compositeParent = null; try { if (!UIComponent.isCompositeComponent(source)) { compositeParent = UIComponent.getCompositeComponentParent(source); } if (compositeParent != null) { compositeParent.pushComponentToEL(context, null); } source.pushComponentToEL(context, null); source.broadcast(rowEvent);
        Hide
        Ted Goddard added a comment -

        It looks like UIData calls getRowIndex() – does our DataTable return the rowIndex into the filtered data or into the unfiltered data? It also looks like we have an opportunity during broadcast() to filter the data rather than calling

        setDataModel(null);

        Show
        Ted Goddard added a comment - It looks like UIData calls getRowIndex() – does our DataTable return the rowIndex into the filtered data or into the unfiltered data? It also looks like we have an opportunity during broadcast() to filter the data rather than calling setDataModel(null);
        Hide
        Nils Lundquist added a comment - - edited

        Judging from:

        int oldRowIndex = getRowIndex();
        setRowIndex(revent.getRowIndex());

        The data table probably has an iterative ID because it has a pre-existing row index that needs saving. When an iterative ID is set, the generator properties don't work correctly, so the unfiltered dataset is being referenced.

        I suppose the only fixes are to override the UIData broadcast behaviour or fix generator properties.

        Show
        Nils Lundquist added a comment - - edited Judging from: int oldRowIndex = getRowIndex(); setRowIndex(revent.getRowIndex()); The data table probably has an iterative ID because it has a pre-existing row index that needs saving. When an iterative ID is set, the generator properties don't work correctly, so the unfiltered dataset is being referenced. I suppose the only fixes are to override the UIData broadcast behaviour or fix generator properties.
        Hide
        Nils Lundquist added a comment - - edited

        Revision #29269
        Committed by nils.lundquist
        A minute ago
        ICE-8098 - Overrode UIData.broadcast() in ace:datatable to correct clientId before accessing filtered state.

        Show
        Nils Lundquist added a comment - - edited Revision #29269 Committed by nils.lundquist A minute ago ICE-8098 - Overrode UIData.broadcast() in ace:datatable to correct clientId before accessing filtered state.

          People

          • Assignee:
            Nils Lundquist
            Reporter:
            Arran Mccullough
          • Votes:
            1 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: