ICEfaces
  1. ICEfaces
  2. ICE-6560

New ace:tabs dynamic tabset component

    Details

    • Type: New Feature New Feature
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 2.0.0
    • Fix Version/s: None
    • Component/s: ACE-Components
    • Labels:
      None
    • Environment:
      ACE
    • Affects:
      Documentation (User Guide, Ref. Guide, etc.), Sample App./Tutorial

      Description

      Currently, there's no built-in way to have a dynamic number of tabs in ace:tabSet. This was intentional, as we didn't want ace:tabSet to be a UIData, but rather we wanted to have a separate component that would control a set of tabPane components. The idea being that we could mix and match tabs and tabPane components together, much like ice:menuItem and ice:menuItems within an ice:menuBar/ice:menuPopup. Most likely we would not want to force the developers to use Java code to create their ace:tabs children, like how ice:menuItems does though. It should be possible to do it via markup:

      <ace:tabSet>
          <ace:tabs value="..." var="myVar">
              <ace:tabPane property="#{myVar.something}>

      Right now, the existing solution is to use c:forEach to create a set of ace:tabPane components. There are several problems with using c:forEach in a dynamic way. It's more meant to be configurably static, rather than truly dynamic. By this I mean that when the page first loads, it may use some criteria to dynamically create the components. But from then on, the components should not be added to, removed, or reordered. What happens is that when the list of components grows or shrinks, everything gets shifted, not truly inserted or removed. Input components, bound to ValueExpressions which are derived from the c:forEach var tend to behave incorrectly after the list is modified. As well, c:forEach creates actual components, instead of stamping out the same one, like a UIData does. This has pros and cons. ACE components are mostly resistant to UIData cons, so we're left with having a greater memory footprint with c:forEach, than a UIData.

      Something else we'd like to have, is a departure from a dumb javax.faces.model.DataModel base iterative container, which is typically driven by a List. Instead, we'd like to move to a more robust data model, which would inherently support insert, remove, reorder operations. The idea being that these data model operations could then drive dom differencing and client side optimisations. For example, when a tabPane is removed from the tabs container, if the data model remove operation is used by the application, then the client would be able to remove that tabPane without having to re-render or re-transmit any of the other tabPane components.

      This more advanced data model could then be built upon to be the basis for the rich data grid, and other iterative containers.

        Activity

        Hide
        Mark Collette added a comment -

        Ted had an idea for a structure of markup that may facilitate adding children. Essentially it involves treating the children markup as a linked list, and not so much as a regular list.

        ----------

        We have a list of tab panels, typically represented as follows

        <div id="tabs">
        <div id="tab0" />
        <div id="tab1" />
        </div>

        The problem arises when this is updated to include an additional
        tab, such as

        <div id="tabs">
        <div id="tab0" />
        <div id="tab1" />
        <div id="tab2" />
        </div>

        Subtree replacement requires that the "tabs" <div> be replaced,
        resulting in a larger update than desired. This can be solved
        by introducing an "insert" operation, however, the detection of
        "insert" cases will have a high CPU cost due to the fact that
        a changing child count will no longer be a short-circuit case.
        (It may be better to have "insert" detection be the responsibility
        of the component, but that's for a different discussion.)

        By rendering the tab list in a slightly different way, however,
        we can retain efficient subtree replacement. The essential idea
        is to trade depth for width:

        <div id="tabs">
        <div id="tab0" />
        <span id="tab0_next">
        <div id="tab1" />
        <span id="tab1_next">
        </span>
        </span>
        </div>

        When tab2 is added the markup becomes:

        <div id="tabs">
        <div id="tab0" />
        <span id="tab0_next">
        <div id="tab1" />
        <span id="tab1_next">
        <div id="tab2" />
        <span id="tab2_next">
        </span>
        </span>
        </span>
        </div>

        If a tab is deleted, the contents of the <div> are removed and
        display is disabled, but the markup remains otherwise untouched.
        If a tab is added, only innermost <span> requires update.

        To assist with the rendering process, deleted tabs are represented
        as null entries in the List of tabs, it is not permitted for the
        application to collapse the List.

        Note that this avoids growing a block of available nodes as per the
        technique of keeping a linear list but with available nodes.

        This technique could be extended to allow insertion at arbitrary
        points in the list as well as appends, but that would require
        additional "framing" markup that may not always be required.

        Ted.

        Show
        Mark Collette added a comment - Ted had an idea for a structure of markup that may facilitate adding children. Essentially it involves treating the children markup as a linked list, and not so much as a regular list. ---------- We have a list of tab panels, typically represented as follows <div id="tabs"> <div id="tab0" /> <div id="tab1" /> </div> The problem arises when this is updated to include an additional tab, such as <div id="tabs"> <div id="tab0" /> <div id="tab1" /> <div id="tab2" /> </div> Subtree replacement requires that the "tabs" <div> be replaced, resulting in a larger update than desired. This can be solved by introducing an "insert" operation, however, the detection of "insert" cases will have a high CPU cost due to the fact that a changing child count will no longer be a short-circuit case. (It may be better to have "insert" detection be the responsibility of the component, but that's for a different discussion.) By rendering the tab list in a slightly different way, however, we can retain efficient subtree replacement. The essential idea is to trade depth for width: <div id="tabs"> <div id="tab0" /> <span id="tab0_next"> <div id="tab1" /> <span id="tab1_next"> </span> </span> </div> When tab2 is added the markup becomes: <div id="tabs"> <div id="tab0" /> <span id="tab0_next"> <div id="tab1" /> <span id="tab1_next"> <div id="tab2" /> <span id="tab2_next"> </span> </span> </span> </div> If a tab is deleted, the contents of the <div> are removed and display is disabled, but the markup remains otherwise untouched. If a tab is added, only innermost <span> requires update. To assist with the rendering process, deleted tabs are represented as null entries in the List of tabs, it is not permitted for the application to collapse the List. Note that this avoids growing a block of available nodes as per the technique of keeping a linear list but with available nodes. This technique could be extended to allow insertion at arbitrary points in the list as well as appends, but that would require additional "framing" markup that may not always be required. Ted.
        Hide
        Mark Collette added a comment -

        The linked list markup approach may be an alternative, or work in conjunction with, the concept of the data model feeding operations to the dom diff, which would drive insert and delete bridge operations, instead of just relying on our existing bridge replace operation.

        Show
        Mark Collette added a comment - The linked list markup approach may be an alternative, or work in conjunction with, the concept of the data model feeding operations to the dom diff, which would drive insert and delete bridge operations, instead of just relying on our existing bridge replace operation.
        Hide
        Adnan Durrani added a comment -

        The ace tabset uses YUI2's tabset, which requires to render certain markup. So I don't think if YUI2 tabset would be happy with suggested markup.

        Show
        Adnan Durrani added a comment - The ace tabset uses YUI2's tabset, which requires to render certain markup. So I don't think if YUI2 tabset would be happy with suggested markup.

          People

          • Assignee:
            Unassigned
            Reporter:
            Mark Collette
          • Votes:
            2 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated: