ICEfaces-EE
  1. ICEfaces-EE
  2. IPCK-308

Column rendering issue with RichDataGrid component

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Cannot Reproduce
    • Affects Version/s: EE-2.0.0
    • Fix Version/s: EE-3.2.0.GA
    • Component/s: Facelet Components
    • Labels:
      None
    • Environment:
      All
    • Assignee Priority:
      P1
    • Salesforce Case Reference:
    • Workaround Exists:
      Yes
    • Workaround Description:
      Call FacesContext.getCurrentInstance().getViewRoot().getChildren().clear() before rendering the 2nd table.

      Description

      Under certain circumstances the columns in a RichDataGrid EE component can be displayed incorrectly, either columns are duplicated or are missing. The scenario for this is the following:

      Two tables (richDataGrid) can show data. These tables are not shown immediately so they are initialized with dummy data. When the tables are required to be shown they are re-initialized with a new column array and a new set of data. The tables are shown one at a time. The issue arises once one table is shown, then another table is rendered. The first table that is rendered then is displayed with either a duplication of columns or is missing columns from when it was first displayed.

      This issue also depends on how the tables are defined in the page. For example, table #1 is displayed first then table #2. If table #2 is rendered first, then table #1, this issue is shown. If table #1 is shown then table #2, this issue does not occur. It also seems to occur on the first rendering of the tables. If the tables are opened in the order that does not show the issue, displaying the tables after using the order that causes the issue does not reproduce the issue.

      Upon further analysis this issue seems to be due to the re-initialization of the RichDataGridBean object with a new column array. In my test case if I re-use the same column array from when it was first initialized, I can't reproduce the issue. This could be a potential workaround as long as the same column list can be used for the data once it has been re-initialized.

        Activity

        Hide
        Arran Mccullough added a comment -

        Steps to reproduce issue:

        • Deploy app in Tomcat 6/Load app
        • Click on the Show New Button, a RichDataGrid will be shown with 4 columns.
        • Click on the Show Existing Button, another RichDataGrid will be shown with the same 4 columns, but the New Table is now shown with only two columns.
        Show
        Arran Mccullough added a comment - Steps to reproduce issue: Deploy app in Tomcat 6/Load app Click on the Show New Button, a RichDataGrid will be shown with 4 columns. Click on the Show Existing Button, another RichDataGrid will be shown with the same 4 columns, but the New Table is now shown with only two columns.
        Hide
        Philip Breau added a comment -

        I would mark this as invalid if the column restructuring is done through manipulating the array returned from RichDataGrid.getColumns(). The component bean is designed to be immutable, so if the user needs to dynamically alter the column structure, a new instance of the bean should be created by calling the constructor again. I have updated the bean, so that a safe copy of the column structure is returned by RichDataGrid.getColumns() preventing any changes to the initial column structure.

        Show
        Philip Breau added a comment - I would mark this as invalid if the column restructuring is done through manipulating the array returned from RichDataGrid.getColumns(). The component bean is designed to be immutable, so if the user needs to dynamically alter the column structure, a new instance of the bean should be created by calling the constructor again. I have updated the bean, so that a safe copy of the column structure is returned by RichDataGrid.getColumns() preventing any changes to the initial column structure.
        Hide
        Philip Breau added a comment - - edited

        I've added the following method that can be used instead of manually manipulating the column array structure:

        /**

        • Returns a copy of the RichDataGridBean with a new column structure.
        • @param columns The new column structure
        • @return a new copy of the bean with the new column structure
          */
          public RichDataGridBean cloneWithNewColumnStructure(IRichDataColumn[] columns)
        Show
        Philip Breau added a comment - - edited I've added the following method that can be used instead of manually manipulating the column array structure: /** Returns a copy of the RichDataGridBean with a new column structure. @param columns The new column structure @return a new copy of the bean with the new column structure */ public RichDataGridBean cloneWithNewColumnStructure(IRichDataColumn[] columns)
        Hide
        Philip Breau added a comment -

        getColumns() was not being called. The issue might be due to c:forEach in the composite component not being re-resolved.

        Show
        Philip Breau added a comment - getColumns() was not being called. The issue might be due to c:forEach in the composite component not being re-resolved.
        Hide
        Philip Breau added a comment -

        simplified test case

        Show
        Philip Breau added a comment - simplified test case
        Hide
        Philip Breau added a comment -

        Simplified test case shows correct behaviour

        Show
        Philip Breau added a comment - Simplified test case shows correct behaviour
        Hide
        Philip Breau added a comment -

        source

        Show
        Philip Breau added a comment - source
        Hide
        Jason Williams added a comment -

        The workaround mentioned does not fulfill the requirements of the application using this so that isn't a valid workaround for the issue.

        Show
        Jason Williams added a comment - The workaround mentioned does not fulfill the requirements of the application using this so that isn't a valid workaround for the issue.
        Hide
        Arran Mccullough added a comment -

        Thanks for the source code. My test case had a slightly different setup in the backing beans. I've been able to narrow down the scenario where this issue is still seen. The main issue with my test case seems to be related to two things:

        • The usage of the RichDataTextColumn shownByDefault object to hide/show the column.
        • The order in how the tables are rendered on the page.

        In your attached test case you just use the basic constructor for the RichDataTextColumn object. In my test case it uses the more fine grained one which gives control over if its editable, shown, etc. I found that if I used the same basic constructor but also changed the number of columns that were rendered for each table it worked well without seeing the issue.

        Using the larger constructor doesn't seem to be the only factor, it also depends on the order they are rendered on the page in relation to where they are defined in the source code of the page.

        For example, in the source code I have the "New" table defined first and then the "Existing" table defined next. If I click the button to show the "New" table and then the "Existing" table, the columns for each one render fine with no missing or duplicated tables. Going the other way, rendering the "Existing" table then the "New" table causes the "Existing table to lose some columns and also have it duplicate the columns.

        Looking at the response for this scenario, for some reason when the "New" table is added, the "Existing" table also gets updated even though it hasn't changed and the action is done on a completely different bean.

        I'm checking to see if there are some workarounds that involve using an <f:ajax> so that only a section of the page is updated instead of the entire form. I should also note that I don't see the same issue with the order of rendering when using the basic constructor. I'm also using the cloneWithNewColumnStructure() method.

        So there would be two potential workarounds:

        • Use the basic constructor for the RichDataTextColumn objects.
        • Control how the tables are rendered on the page.
        Show
        Arran Mccullough added a comment - Thanks for the source code. My test case had a slightly different setup in the backing beans. I've been able to narrow down the scenario where this issue is still seen. The main issue with my test case seems to be related to two things: The usage of the RichDataTextColumn shownByDefault object to hide/show the column. The order in how the tables are rendered on the page. In your attached test case you just use the basic constructor for the RichDataTextColumn object. In my test case it uses the more fine grained one which gives control over if its editable, shown, etc. I found that if I used the same basic constructor but also changed the number of columns that were rendered for each table it worked well without seeing the issue. Using the larger constructor doesn't seem to be the only factor, it also depends on the order they are rendered on the page in relation to where they are defined in the source code of the page. For example, in the source code I have the "New" table defined first and then the "Existing" table defined next. If I click the button to show the "New" table and then the "Existing" table, the columns for each one render fine with no missing or duplicated tables. Going the other way, rendering the "Existing" table then the "New" table causes the "Existing table to lose some columns and also have it duplicate the columns. Looking at the response for this scenario, for some reason when the "New" table is added, the "Existing" table also gets updated even though it hasn't changed and the action is done on a completely different bean. I'm checking to see if there are some workarounds that involve using an <f:ajax> so that only a section of the page is updated instead of the entire form. I should also note that I don't see the same issue with the order of rendering when using the basic constructor. I'm also using the cloneWithNewColumnStructure() method. So there would be two potential workarounds: Use the basic constructor for the RichDataTextColumn objects. Control how the tables are rendered on the page.
        Hide
        Arran Mccullough added a comment -

        Using an <h:commandButton> that uses an <f:ajax>tag and also testing on the final ICEfaces EE 3.0 release looks to have helped to resolve the remaining issue that I have seen. I replaced the ice:commandButton that toggled the display of the richDataGrid with an h:commandButton that uses an f:ajax tag to render only the panel that surrounds the richDataGrid component. This prevents the update from affecting the other richDataGrid.

        Here is the code I've been testing:

        <h:form id="iceForm">
        <h:panelGroup id="newPanel">
        <ice:commandButton value="Show New - ICE" actionListener="#

        {testBeanNew.resetList}"/>
        <h:commandButton value="Show New - JSF" actionListener="#{testBeanNew.resetList}

        ">
        <f:ajax execute="@this" render="iceForm:newPanel"/>
        </h:commandButton>
        <ice:panelGroup rendered="#

        {testBeanNew.showNew}

        ">
        <ice:outputText value="New Table"/>
        <ice-cc:richDataGrid bean="#

        {testBeanNew.richDataGridBean}

        "
        id="richDataGridNew"
        />
        </ice:panelGroup>
        </h:panelGroup>
        <h:panelGroup id="existingPanel">
        <ice:commandButton value="Show Existing - ICE" actionListener="#

        {testBeanExisting.resetList}"/>
        <h:commandButton value="Show Existing - JSF" actionListener="#{testBeanExisting.resetList}

        ">
        <f:ajax execute="@this" render="iceForm:existingPanel"/>
        </h:commandButton>
        <ice:panelGroup rendered="#

        {testBeanExisting.showExisting}

        ">
        <ice:outputText value="Existing Table"/>
        <ice-cc:richDataGrid bean="#

        {testBeanExisting.richDataGridBean}

        "
        id="richDataGridExisting"
        />
        </ice:panelGroup>
        </h:panelGroup>
        </h:form>

        When using the ice:commandButton's and showing the "Existing" table first and then the "New" table, the "Existing" table columns would be not correct. When using the h:commandButton's in the same way, the "Existing" table keeps its correct set of columns as the update doesn't effect this panel.

        Show
        Arran Mccullough added a comment - Using an <h:commandButton> that uses an <f:ajax>tag and also testing on the final ICEfaces EE 3.0 release looks to have helped to resolve the remaining issue that I have seen. I replaced the ice:commandButton that toggled the display of the richDataGrid with an h:commandButton that uses an f:ajax tag to render only the panel that surrounds the richDataGrid component. This prevents the update from affecting the other richDataGrid. Here is the code I've been testing: <h:form id="iceForm"> <h:panelGroup id="newPanel"> <ice:commandButton value="Show New - ICE" actionListener="# {testBeanNew.resetList}"/> <h:commandButton value="Show New - JSF" actionListener="#{testBeanNew.resetList} "> <f:ajax execute="@this" render="iceForm:newPanel"/> </h:commandButton> <ice:panelGroup rendered="# {testBeanNew.showNew} "> <ice:outputText value="New Table"/> <ice-cc:richDataGrid bean="# {testBeanNew.richDataGridBean} " id="richDataGridNew" /> </ice:panelGroup> </h:panelGroup> <h:panelGroup id="existingPanel"> <ice:commandButton value="Show Existing - ICE" actionListener="# {testBeanExisting.resetList}"/> <h:commandButton value="Show Existing - JSF" actionListener="#{testBeanExisting.resetList} "> <f:ajax execute="@this" render="iceForm:existingPanel"/> </h:commandButton> <ice:panelGroup rendered="# {testBeanExisting.showExisting} "> <ice:outputText value="Existing Table"/> <ice-cc:richDataGrid bean="# {testBeanExisting.richDataGridBean} " id="richDataGridExisting" /> </ice:panelGroup> </h:panelGroup> </h:form> When using the ice:commandButton's and showing the "Existing" table first and then the "New" table, the "Existing" table columns would be not correct. When using the h:commandButton's in the same way, the "Existing" table keeps its correct set of columns as the update doesn't effect this panel.
        Hide
        Jason Williams added a comment -

        The problem initially occurred when logic triggered while clicking on ice:treeNode which cannot be replaced with an h:commandButton so I don't understand how that work around will correct this issue.

        What happens was the user clicks on this tree node which brings up either one or both richdatagrids based on some conditions. Intermittently this duplicate column issue would appear when that occurs. I don't know if the test case has been simplified too much which removes the treeNode but that is an integral part of the application and cannot be replaced with a button.

        Show
        Jason Williams added a comment - The problem initially occurred when logic triggered while clicking on ice:treeNode which cannot be replaced with an h:commandButton so I don't understand how that work around will correct this issue. What happens was the user clicks on this tree node which brings up either one or both richdatagrids based on some conditions. Intermittently this duplicate column issue would appear when that occurs. I don't know if the test case has been simplified too much which removes the treeNode but that is an integral part of the application and cannot be replaced with a button.
        Hide
        Philip Breau added a comment -

        Upon investigation I noticed that many autogenerated jsf component ids were changing at the rerendering of the new data grid. I've added more explicitly set dynamic id's to the sub components of the rich data grid to avoid this, although the update issue is still happening.

        The column data on the server appears to be correct. On clicking the 'Show New' button to render the new table, the columns of the existing table are resolved to be the following (column name, visibility state):

        columns [ id:true, firstName:true, lastName:true, dept:true, section:false, company:false ]

        I've ruled out partial update anomalies. The DOM updates are being reconstructed properly, although the DOM updates themselves are being generated incorrectly. eg: one of the updates occurring on the rerendering of the new table is the following column data:

        <div class="icePnlGrp richDatGrdCol"
        id="iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ctr"
        oncontextmenu="Ice.Menu.contextMenuPopup(event,
        'iceForm:richDataGridNew_subview:richDataGridNew:1:createRowMenu_sub',
        'iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ctr');return
        false;"
        style="width:;">
        <span class="iceOutTxt"
        id="iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ro"
        title="Johnson">Johnson</span>
        </div>

        Looking at the update, this is the last name cell value of 'Johnson'. Although the id's are incorrect. The id 'iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ro' should belong to the cell of the second row first column, named 'id', a text column, in the read only state. So, it appears that the outputText for the 'id' column, has the value set for the last name column.

        So, since this doesn't appear to be a partial update or DOM diff issue, it points to a state saving issue on the component level. Further investigation is required. An interim workaround that could be provided very quickly is creating a 2nd separate richDataGrid component, with a different set of internally rendering id's, which may, perhaps, shield the components from any variable scoping issues.

        Show
        Philip Breau added a comment - Upon investigation I noticed that many autogenerated jsf component ids were changing at the rerendering of the new data grid. I've added more explicitly set dynamic id's to the sub components of the rich data grid to avoid this, although the update issue is still happening. The column data on the server appears to be correct. On clicking the 'Show New' button to render the new table, the columns of the existing table are resolved to be the following (column name, visibility state): columns [ id:true, firstName:true, lastName:true, dept:true, section:false, company:false ] I've ruled out partial update anomalies. The DOM updates are being reconstructed properly, although the DOM updates themselves are being generated incorrectly. eg: one of the updates occurring on the rerendering of the new table is the following column data: <div class="icePnlGrp richDatGrdCol" id="iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ctr" oncontextmenu="Ice.Menu.contextMenuPopup(event, 'iceForm:richDataGridNew_subview:richDataGridNew:1:createRowMenu_sub', 'iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ctr');return false;" style="width:;"> <span class="iceOutTxt" id="iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ro" title="Johnson">Johnson</span> </div> Looking at the update, this is the last name cell value of 'Johnson'. Although the id's are incorrect. The id 'iceForm:richDataGridNew_subview:richDataGridNew:1:id_text_ro' should belong to the cell of the second row first column, named 'id', a text column, in the read only state. So, it appears that the outputText for the 'id' column, has the value set for the last name column. So, since this doesn't appear to be a partial update or DOM diff issue, it points to a state saving issue on the component level. Further investigation is required. An interim workaround that could be provided very quickly is creating a 2nd separate richDataGrid component, with a different set of internally rendering id's, which may, perhaps, shield the components from any variable scoping issues.
        Hide
        Philip Breau added a comment -

        Still looking for a solution to this, although the issue still appears to be a JSTL scoping and possibly a JSF component id issue. One current simple workaround is to call FacesContext.getCurrentInstance().getViewRoot().getChildren().clear(); before rendering the second table:

        class TestBeanNew{
        ...
        public void resetList(ActionEvent event)

        { this.showExisting = !this.showExisting; BaseRichDataColumn[] columns = new BaseRichDataColumn[6]; columns[0] = new RichDataTextColumn(Integer.class, "id", "ID1", false, false, true, false, false); columns[1] = new RichDataTextColumn(String.class, "firstName", "First Name1", true, true, true, false, true); columns[2] = new RichDataTextColumn(String.class, "lastName", "Last Name1", true, true, true, true, true); columns[3] = new RichDataTextColumn(String.class, "dept", "Department1", true, true, true, true, true); columns[4] = new RichDataTextColumn(String.class, "section", "Section1", true, true, true, true, true); columns[5] = new RichDataTextColumn(String.class, "company", "Company1", true, true, false, false, true); RichDataBooleanRowProperty editableRowProperty = new RichDataBooleanRowProperty(Employee.class, "editable"); setRichDataGridBean(new RichDataGridBean(empList, columns, editableRowProperty, new MyTableEventListener())); FacesContext.getCurrentInstance().getViewRoot().getChildren().clear(); }

        ...
        }

        Clearing the JSF component children of the panelGroup containing the new data grid In the test case should also be a workaround.

        Show
        Philip Breau added a comment - Still looking for a solution to this, although the issue still appears to be a JSTL scoping and possibly a JSF component id issue. One current simple workaround is to call FacesContext.getCurrentInstance().getViewRoot().getChildren().clear(); before rendering the second table: class TestBeanNew{ ... public void resetList(ActionEvent event) { this.showExisting = !this.showExisting; BaseRichDataColumn[] columns = new BaseRichDataColumn[6]; columns[0] = new RichDataTextColumn(Integer.class, "id", "ID1", false, false, true, false, false); columns[1] = new RichDataTextColumn(String.class, "firstName", "First Name1", true, true, true, false, true); columns[2] = new RichDataTextColumn(String.class, "lastName", "Last Name1", true, true, true, true, true); columns[3] = new RichDataTextColumn(String.class, "dept", "Department1", true, true, true, true, true); columns[4] = new RichDataTextColumn(String.class, "section", "Section1", true, true, true, true, true); columns[5] = new RichDataTextColumn(String.class, "company", "Company1", true, true, false, false, true); RichDataBooleanRowProperty editableRowProperty = new RichDataBooleanRowProperty(Employee.class, "editable"); setRichDataGridBean(new RichDataGridBean(empList, columns, editableRowProperty, new MyTableEventListener())); FacesContext.getCurrentInstance().getViewRoot().getChildren().clear(); } ... } Clearing the JSF component children of the panelGroup containing the new data grid In the test case should also be a workaround.
        Hide
        Jason Williams added a comment - - edited

        All of the workarounds suggested either don't satisfy the requirements or they don't work. I see the latest EE release was added to the end of this but the issue is still open. Does that mean that code changes were actually made in 3.0.0.GA_P01 and it works or does that mean just try one of the workarounds that doesn't work?

        Show
        Jason Williams added a comment - - edited All of the workarounds suggested either don't satisfy the requirements or they don't work. I see the latest EE release was added to the end of this but the issue is still open. Does that mean that code changes were actually made in 3.0.0.GA_P01 and it works or does that mean just try one of the workarounds that doesn't work?
        Hide
        Philip Breau added a comment -

        Retested on ICEfaces EE 3 rev 33253 with or without the workaround. Note that this build is now using Mojarra 2.1.17, which includes state saving bug fixes that have may have been the root issue here.

        Show
        Philip Breau added a comment - Retested on ICEfaces EE 3 rev 33253 with or without the workaround. Note that this build is now using Mojarra 2.1.17, which includes state saving bug fixes that have may have been the root issue here.

          People

          • Assignee:
            Philip Breau
            Reporter:
            Arran Mccullough
          • Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: