ICEfaces
  1. ICEfaces
  2. ICE-6728

Add client-side cache support for ace:tabPane within a 'clientSide=false'

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.0.0
    • Fix Version/s: 2.1-Beta, 3.0, EE-2.0.0.GA_P01
    • Component/s: ACE-Components
    • Labels:
      None
    • Environment:
      ICEfaces 2, ace:tabSet component
    • Assignee Priority:
      P2
    • Affects:
      Documentation (User Guide, Ref. Guide, etc.), Compatibility/Configuration

      Description

      A useful enhancement for the ace:tabSet and ace:tabPane would be to support an optional "clientCache" configuration on the tabPane component that would be used when the tabSet was in 'clientSide=false' mode. In this mode, as each tab is selected its tabPane contents are normally rendered on the server and updated on the client, providing a fully dynamic tabSet with only a single tabPane actually being rendered to the client at one time.

      By specifying the new 'clientCache=true' attribute on a particular tabPane, once that tabPane had initially been rendered (by being selected), it would remain rendered on the client and subsequent selections of the same tab would not result in its contents being updated by the server. However, tab selection events (and the selected index being updated) would still occur on the server as usual, allowing the application the opportunity to be notified of the tab's selection, etc.

      Putting this attribute on the tabPane instead of the tabSet would provide additional flexibility in cases where this behaviour is only desired on specific tabs. As a convenience, the same attribute could be provided on the tabSet itself to enable setting for all tabs, subject to being overridden by the tabPane's own attribute setting if defined.



        Issue Links

          Activity

          Hide
          Ken Fyten added a comment -

          A typical use-case for this feature would be a tabPane that includes an iframe that itself hosts an external application. Once the app. is loaded into the iframe, you don't want it disappearing and being reloaded, etc. as the tabs are selected or you will lose your state within the hosted app., etc.

          Show
          Ken Fyten added a comment - A typical use-case for this feature would be a tabPane that includes an iframe that itself hosts an external application. Once the app. is loaded into the iframe, you don't want it disappearing and being reloaded, etc. as the tabs are selected or you will lose your state within the hosted app., etc.
          Hide
          Mark Collette added a comment -

          From brainstorming today, we came up with two different approaches. Both try to allow for a dynamic number of tabPanes, each of which could contain JSF components or alternatively an iframe. Once an iframe is loaded, we don't want it to be reloaded. The tabSet would be in a server mode, where tabPane contents are cached in the client, so that the iframe contents won't be updated, and tabPane contents are lazily loaded once they've been visited.

          Harder approach

          The tabSet renders the iframe element right into the tabPane content area. When a tabPane is visited for the first time, the content starts being rendered, whereas before it rendered an empty place-holder. Eventually, if every tabPane has been visited, then every tabPane's contents is being rendered. The dom diff ensures that only changed contents, which includes first time visited contents, will be sent to the browser. We could add a dom diff hint to not check and not send previously visited tabPane contents, so that we wouldn't have to render every visited tabPane's contents per lifecycle. But then the regular JSF taPane's would not really be interactive.

          Adding new tabPanes would cause dom diff to update all contents, since the count would change, so instead we either use empty place-holders for potentially added/inserted tabPanes, or we add insertion and deletion support, which would require dom diff and possibly bridge support. The empty place-holders would increase the rendered output, but not by a lot. The dom diff and bridge support for insertion and deletion could be an extensive effort. Bot would require component support. The application would need to inform the tabSet of inserted or removed tabPane components.

          Simpler approach

          The tabSet is used in regular server side mode. It does not specially render out place-holders for unvisited or to-be-added tabPanes. Changes to the count of tabPanes will cause the content area to all be updated. But, the cached contents are not actually rendered into there, they are instead rendered into a separate area, and made invisible.

          For the iframe scenario, we would use Ted's technique of a linked list of iframe elements, which can be added to without affecting a child count, using only dom replace operations, and which can be deleted from, by simply retargeting the iframe to a blank location, and ceasing to use it further.

          <div id="cachedCont_1">
          <iframe id="cached_1"/>
          <div id="cachedCont_2>
          <iframe id="cached_2/>
          <div id="cachedCont_3>
          ...
          </div>
          </div>
          </div>

          In the javascript, we then listen for tab change events, and then place the iframe element inside the content area. Either by using absolute positioning, or by actually reparenting the element. Then make it visible. When tabbing away, hide it, and reparent it back if that's how we got it there. When an iframe tabPane is first visited, then it's added to the linked list and rendered into it. From then on we have to continue rendering that iframe, so it won't be dom differenced. Or we could add that don't-check-don't-update hint. We might not need a client side tab change listener if we just render that javascript from the server side as the selected tabPane is changed.

          We can generalise from iframe elements to any element, so that certain tabPanes can be marked as caching, so their contents are rendered into the protected area. Then we'd be manipulating arbitrary elements instead of iframes. Or could detect when the elements are iframes, if they behave as a special case.

          Most likely, while all of that can be automated, the application will still need to reveal to the tabSet component, when tabPanes have been inserted or removed. This is because, if a c:forEach is being use to build the tabPanes, their ids will ripple change when the tabPane list changes. Some method should be investigated for making the ids stick, so that they're not index based. If the ids were fixed, from using a data model id instead of index or generated id, then the tabSet could detect newly visited tabPanes and removed tabPanes itself.

          Show
          Mark Collette added a comment - From brainstorming today, we came up with two different approaches. Both try to allow for a dynamic number of tabPanes, each of which could contain JSF components or alternatively an iframe. Once an iframe is loaded, we don't want it to be reloaded. The tabSet would be in a server mode, where tabPane contents are cached in the client, so that the iframe contents won't be updated, and tabPane contents are lazily loaded once they've been visited. Harder approach The tabSet renders the iframe element right into the tabPane content area. When a tabPane is visited for the first time, the content starts being rendered, whereas before it rendered an empty place-holder. Eventually, if every tabPane has been visited, then every tabPane's contents is being rendered. The dom diff ensures that only changed contents, which includes first time visited contents, will be sent to the browser. We could add a dom diff hint to not check and not send previously visited tabPane contents, so that we wouldn't have to render every visited tabPane's contents per lifecycle. But then the regular JSF taPane's would not really be interactive. Adding new tabPanes would cause dom diff to update all contents, since the count would change, so instead we either use empty place-holders for potentially added/inserted tabPanes, or we add insertion and deletion support, which would require dom diff and possibly bridge support. The empty place-holders would increase the rendered output, but not by a lot. The dom diff and bridge support for insertion and deletion could be an extensive effort. Bot would require component support. The application would need to inform the tabSet of inserted or removed tabPane components. Simpler approach The tabSet is used in regular server side mode. It does not specially render out place-holders for unvisited or to-be-added tabPanes. Changes to the count of tabPanes will cause the content area to all be updated. But, the cached contents are not actually rendered into there, they are instead rendered into a separate area, and made invisible. For the iframe scenario, we would use Ted's technique of a linked list of iframe elements, which can be added to without affecting a child count, using only dom replace operations, and which can be deleted from, by simply retargeting the iframe to a blank location, and ceasing to use it further. <div id="cachedCont_1"> <iframe id="cached_1"/> <div id="cachedCont_2> <iframe id="cached_2/> <div id="cachedCont_3> ... </div> </div> </div> In the javascript, we then listen for tab change events, and then place the iframe element inside the content area. Either by using absolute positioning, or by actually reparenting the element. Then make it visible. When tabbing away, hide it, and reparent it back if that's how we got it there. When an iframe tabPane is first visited, then it's added to the linked list and rendered into it. From then on we have to continue rendering that iframe, so it won't be dom differenced. Or we could add that don't-check-don't-update hint. We might not need a client side tab change listener if we just render that javascript from the server side as the selected tabPane is changed. We can generalise from iframe elements to any element, so that certain tabPanes can be marked as caching, so their contents are rendered into the protected area. Then we'd be manipulating arbitrary elements instead of iframes. Or could detect when the elements are iframes, if they behave as a special case. Most likely, while all of that can be automated, the application will still need to reveal to the tabSet component, when tabPanes have been inserted or removed. This is because, if a c:forEach is being use to build the tabPanes, their ids will ripple change when the tabPane list changes. Some method should be investigated for making the ids stick, so that they're not index based. If the ids were fixed, from using a data model id instead of index or generated id, then the tabSet could detect newly visited tabPanes and removed tabPanes itself.
          Hide
          Mark Collette added a comment -

          The animation issue has been spun off into ICE-6961.

          Show
          Mark Collette added a comment - The animation issue has been spun off into ICE-6961 .
          Hide
          Mark Collette added a comment -

          Fixed the ui:repeat issue, since Art fixed the @Field generation issue, so I switched the TabSetMeta class to use @Field instead of @Property for the visitedTabClientIds, which side-steps the whole issue of setting a @Property in Render phase as not quite working properly.

          Fixed the client-side issue of the non-selected contents showing briefly, by making them be pre-styled to be hidden.

          icefaces2/trunk/icefaces
          Subversion 24840

          icefaces2/branches/icefaces-2.0.x-maintenance/icefaces
          Subversion 24842

          Show
          Mark Collette added a comment - Fixed the ui:repeat issue, since Art fixed the @Field generation issue, so I switched the TabSetMeta class to use @Field instead of @Property for the visitedTabClientIds, which side-steps the whole issue of setting a @Property in Render phase as not quite working properly. Fixed the client-side issue of the non-selected contents showing briefly, by making them be pre-styled to be hidden. icefaces2/trunk/icefaces Subversion 24840 icefaces2/branches/icefaces-2.0.x-maintenance/icefaces Subversion 24842
          Hide
          Mark Collette added a comment -

          Merged these commits to the development branch. The javascript had to be adapted because we had since pulled out YUI3 use, which this code had relied on. As well, style class renaming for themeroller necessitated some changed as well.

          Development branch
          Subversion 26336

          Show
          Mark Collette added a comment - Merged these commits to the development branch. The javascript had to be adapted because we had since pulled out YUI3 use, which this code had relied on. As well, style class renaming for themeroller necessitated some changed as well. Development branch Subversion 26336
          Hide
          Mark Collette added a comment -

          Had to revert the javascript pre-hiding, since that was breaking tabSetProxy. Since it didn't fix ICE-7435 anyway, this shouldn't matter.

          Development branch
          Subversion 26414
          icefaces2/ace/component/resources/icefaces.ace/tabset/tabset.js

          Show
          Mark Collette added a comment - Had to revert the javascript pre-hiding, since that was breaking tabSetProxy. Since it didn't fix ICE-7435 anyway, this shouldn't matter. Development branch Subversion 26414 icefaces2/ace/component/resources/icefaces.ace/tabset/tabset.js

            People

            • Assignee:
              Mark Collette
              Reporter:
              Ken Fyten
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: