Details
-
Type: New Feature
-
Status: Open
-
Priority: 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.
<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.
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.