By Duncan Mills on Apr 15, 2013
In the first part of this series I described the basic physical makeup of the revealPanel and described a simple server side approach to the display and hide of the information panel and indicator triangle.
The sample that you can download shows both approaches.
This Object does not directly hold the information about the panel, it's a slightly more complex structure:
- revealPanelGroupMap is a Map (thinking in Java terms) of revealGroup Objects
- Each RevealGroup contains an array of Topics and the currently selected Topic in the array
- Each Topic contains component references to the triangle PGL and the revealPanel PGL as well as the topic number
How are the Data Structures Created?
So this revealPanelGroupMap does not appear out of thin air, but neither in fact is it created in advance in any way. Because, for an individual page, you may have dynamic regions and suchlike, I wanted the revealPanels to be self registering and created on demand rather than pre-defined in some way. The way that I ended up achieving this was to add a client attribute to each of the three important panelGroupLayouts (the topic, the triangle and the revealPanel). This locator is just a string consisting of 3 parts delimited by ":".
- The panelGroupId - e.g. "REVEAL1"
- The TopicID - zero indexed
- The SegmentID - each topic has three segments 0-2 where 0 is the topic, 1 is the triangle and 2 is the revealPanel
So the client attribute definition in the page looks like this:
<af:clientAttribute name="revealPanelLocator" value="REVEAL1:0:0"/>
This indicates that this panel is the topic panel for the first topic in the REVEAL1 group.
When the first of the topics is clicked within a revealPanel, the click handler code grabs this locator and parses out the segments. From the first segment it can tell if this group has been seen before, if it has then great, it can start to set visibility etc. If, however, the group is not present in the main map then we do a little self discovery to find out how many Topics there are in the revealPanel group as a whole and store the relevant component references.
Walking the Tree
So how to we self discover the group? Well the first bit is easy. Using the getParent() API from the component that raised the click event we can get a reference to the hosting panelGridLayout.
I then use the visitChildren() api on the grid to visit each of its top level children. Each of these will actually be one of those segment panelGroupLayouts. So in a panelGrid with 9 children that will actually define 3 topics each consisting of three panels (topic, triangle, reveal) . As each of those panels has an associated clientAttribute with it's locator we can add it to the correct place in the revealGroup control structure.
The visitChildren API actually defines a callback function that the framework will call for you, passing any context that you've defined and the child component it has found.
Note, it would be perfectly possible to alter the sample code to do away with the clientAttribute all together. I actually wrote it this way to allow for more than one revealPanel to be hosted within the same physical panelGrid. However, of that's not something you need then the page definition could be condensed down to simply the clientListener in the Topic panel. Left as an exercise for the reader...
Acting on the Click
Once the data structure is build for a particular revealPanel it will be caching the component references to the triangle and to the reveal PGL so you can simply call setVisible(true|false) to manage the hide and display.
You've Noticed the Fatal Flaw?
Well I hope so... If I'm storing the component references to the various panelGroupLayouts will they stay valid? Well certainly if I navigate off to another page no. But that's OK, because if I navigate away then the document object in the DOM will have been destroyed, taking my data structures with it. So when I re-enter the page they just get re-built. So no problems there. However, if my page contains a region and one of the fragments on the region has a revealPanel then there is a problem. Changing the view in the region will not effect the overall document so the control structures contain references to components that have been destroyed - if I go away and come back to a revealGroup on a fragment then the references will be bad. However, there is a simple solution to this. The client side components have an isDead() API call which we can use to check to see if the reference held in the cache is still valid. If it is then great. If not we assume that we need to rebuild the entire structure for that revealPanel.
Adding Some Bling with Animations
I promised some animation as part of this version and indeed it's in there. So in fact, rather than simply setting the visible attribute when the topic is clicked I do that, but I also apply some CSS styles as well.
I'm essentially re-using some of the things I've discussed before in my postings on animations, using in this case Scale transforms for the main reveal panel and a color transform for the triangle.
Here's the logical flow:
Revealing a Topic
- Set the triangle to visible - it's initial style will define it as transparent
- Set the reveal panel as visible - its initial style will define a scale of (1,0) with full width but no height
- Set the reveal panel style to revealPanelAnimated. This resets the scale to (1,1) - full size over about 200ms
- Register a listener on the animation
- When the animation is finished then reset the style on the triangle to restore its color.
Note that I've used a scale transform here rather than actually trying to move the PGL <div> around the screen. Trying the latter could put you into conflict with the existing layout management and will certainly cause some problems with z-order in the DOM so it's better to avoid the issue by scaling in place. It gives a pleasing effect.
The actual code and styles involved in all of this is a little too complex to reproduce here in the blog, so do be sure to check out the demo. I've added plenty of comments to help you along.