Monday Apr 15, 2013

Building the revealPanel - Part 2

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.  

In this posting I want to look at an alternative approach that uses the same physical components in the UI, but rather than handling the reveal with a roundtrip to the server, does it all in the browser itself using JavaScript.

The sample that you can download shows both approaches. 

Changing the Configuration for JavaScript Use

The JavaScript version of the component works in essentially the same way as the server side version that I talked about last time, that is, it simply sets the visible attribute of the panelGroupLayouts that contain the reveal information and the triangle. The good thing is at this is a property that we can set through the ADF Faces client component JavaScript API and we never really have to tell the server side what the state of the panels is as we'll always revert to the all-closed layout if anything like navigation away and back takes place. 

So the definition of say the triangle PGL simply has the visible attribute set to a hardcoded value of false, with no need to refer to a managed bean. Of course we need to hold the information about which panels are expanded somewhere,  so that we can close one set as another is opened, or close a set if it is currently open. To do this I actually create a new JavaScript object at the page level called revealPanelGroupMap (if you want to read along open the revealPanel.js file in the demo). 

This Object does not directly hold the information about the panel, it's a slightly more complex structure:

  1. revealPanelGroupMap is a Map (thinking in Java terms) of revealGroup Objects 
  2. Each RevealGroup contains an array of Topics and the currently selected Topic in the array
  3. Each Topic contains component references to the triangle PGL and the revealPanel PGL as well as the topic number
The fact that revealPanelGroupMap can hold multiple revealGroups allows us to host multiple revealPanels on the same page as long as each is identified by a unique string which is used to key that revealPanelGroupMap.

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 ":".

  1. The panelGroupId - e.g. "REVEAL1"
  2. The TopicID - zero indexed
  3. 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

  1. Set the triangle to visible - it's initial style will define it as transparent
  2. Set the reveal panel as visible  - its initial style will define a scale of (1,0) with full width but no height
  3. Set the reveal panel style to revealPanelAnimated. This resets the scale to (1,1) - full size over about 200ms
  4. Register a listener on the animation
  5. 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.  

About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2013 »
MonTueWedThuFriSatSun
1
3
4
5
6
7
8
9
10
11
13
14
16
17
18
20
21
22
23
24
25
26
27
28
29
     
       
Today