Tuesday Jul 30, 2013

(Adventures in) ADF Logging in 12c

An offline conversation with Andrejus Baranovskis the other day alerted me to a difference in behavior with the ADF Logger in 12.1.2. It seems that the logging output is not being output to the console as it was in 11g, although it's certainly being written to the log file OK.

I did a little testing and digging on this, and the issue it turns out is pretty simple.

The root cause is in the default definition of the console hander in the logging.xml file. In 11g, this handler is configured to let everything through level="ALL".  In 12c the default configuration is only to pass more significant issues through to the console level="WARNING:32".  The reason for this change is, I'm guessing to reduce the amount of general noise that you see when WebLogic is started. 

So it's trivial to switch back to the verboseness of 11g if you'd like to:

  1. From the console choose Actions --> Configure Oracle Diagnostic Logging to open the logging.xml view
  2. Switch to source view and locate the <log_handler name='console-handler' ... entry
  3. Change the WARNING:32 value to ALL

If you are feeling more creative, and want the best of both worlds, you can of course define a second handler for the console with this more permissive value and assign that explicitly to just your loggers - then you can get the verbose output for your logging without everything else.  

For example;

1. Define a custom handler 

<log_handler name='demo-console-handler' 
             class='oracle.core.ojdl.logging.ConsoleHandler' 
             formatter='oracle.core.ojdl.weblogic.ConsoleFormatter'
             level="ALL"/> 

2. Use the custom handler for your root logger:

<logger name="oracle.demo" level="FINEST" useParentHandlers='false'>
  <handler name='odl-handler'/>
  <handler name='wls-domain'/>
  <handler name='demo-console-handler'/>  
</logger> 

Thursday Sep 27, 2012

PanelGridLayout - A Layout Revolution

With the most recent 11.1.2 patchset (11.1.2.3) there has been a lot of excitement around ADF Essentials (and rightly so), however, in all the fuss I didn't want an even more significant change to get missed - yes you read that correctly, a more significant change! I'm talking about the new panelGridLayout component, I can confidently say that this one of the most revolutionary components that we've introduced in 11g, even though it sounds rather boring. To be totally accurate, panelGrid was introduced in 11.1.2.2 but without any presence in the component palette or other design time support, so it was largely missed unless you read the release notes. However in this latest patchset it's finally front and center. Its time to explore - we (really) need to talk about layout. 

Let's face it,with ADF Faces rich client, layout is a rather arcane pursuit, once you are a layout master, all bow before you, but it's more of an art than a science, and it is often, in fact, way too difficult to achieve what should (apparently) be a pretty simple.

Here's a great example, it's a homework assignment I set for folks I'm teaching this stuff to: 

Sample Layout

The requirements for this layout are:

  1. The header is 80px high, the footer is 30px. These are both fixed. 
  2. The first section of the header containing the logo is 180px wide
  3. The logo is centered within the top left hand corner of the header 
  4. The title text is start aligned in the center zone of the header and will wrap if the browser window is narrowed. It should be aligned in the center of the vertical space 
  5. The about link is anchored to the right hand side of the browser with a 20px gap and again is center aligned vertically. It will move as the browser window is reduced in width.
  6. The footer has a right aligned copyright statement, again middle aligned within a 30px high footer region and with a 20px buffer to the right hand edge. It will move as the browser window is reduced in width.
  7. All remaining space is given to a central zone, which, in this case contains a panelSplitter.
  8. Expect that at some point in time you'll need a separate messages line in the center of the footer. 

In the homework assigment I set I also stipulate that no inlineStyles can be used to control alignment or margins and no use of other taglibs (e.g. JSF HTML or Trinidad HTML). 
So, if we take this purist approach, that basic page layout (in my stock solution) requires 3 panelStretchLayouts, 5 panelGroupLayouts and 4 spacers - not including the spacer I use for the logo and the contents of the central zone splitter - phew! The point is that even a seemingly simple layout needs a bit of thinking about, particulatly when you consider strechting and browser re-size behavior. In fact, this little sample actually teaches you much of what you need to know to become vaguely competant at layouts in the framework. The underlying result of "the way things are" is that most of us reach for panelStretchLayout before even finishing the first sip of coffee as we embark on a new page design. In fact most pages you will see in any moderately complex ADF page will basically be nested panelStretchLayouts and panelGroupLayouts, sometimes many, many levels deep. 
So this is a problem, we've known this for some time and now we have a good solution. (I should point out that the oft-used Trinidad trh tags are not a particularly good solution as you're tie-ing yourself to an HTML table based layout in that case with a host of attendent issues in resize and bi-di behavior, but I digress.)


So, tadaaa, I give to you panelGridLayout. PanelGrid, as the name suggests takes a grid like (dare I say slightly gridbag-like) approach to layout, dividing your layout into rows and colums with margins, sizing, stretch behaviour, colspans and rowspans all rolled in, all without the use of inlineStyle. As such, it provides for a much more powerful and consise way of defining a layout such as the one above that is actually simpler and much more logical to design. The basic building blocks are the panelGridLayout itself, gridRow and gridCell. Your content sits inside the cells inside the rows, all helpfully allowing both streching, valign and halign definitions without the need to nest further panelGroupLayouts. So much simpler! 


If I break down the homework example above my nested comglomorate of 12 containers and spacers can be condensed down into a single panelGrid with 3 rows and 5 cell definitions (39 lines of source reduced to 24 in the case of the sample). What's more, the actual runtime representation in the browser DOM is much, much simpler, and clean, with basically one DIV per cell (Note that just because the panelGridLayout semantics looks like an HTML table does not mean that it's rendered that way!) .


Another hidden benefit is the runtime cost. Because we can use a single layout to achieve much more complex geometries the client side layout code inside the browser is having to work a lot less. This will be a real benefit if your application needs to run on lower powered clients such as netbooks or tablets.


So, it's time, if you're on 11.1.2.2 or above, to smile warmly at your panelStretchLayouts, wrap the blanket around it's knees and wheel it off to the Sunset Retirement Home for a well deserved rest. There's a new kid on the block and it wants to be your friend. 

Update: panelGridLayout is also available in the 11.1.1.7 release as well as the 11.1.2.n series. 

Tuesday Jul 03, 2012

New Sample Demonstrating the Traversing of Tree Bindings

A technique that I seem to use a fair amount, particularly in the construction of dynamic UIs is the use of a ADF Tree Binding to encode a multi-level master-detail relationship which is then expressed in the UI in some kind of looping form – usually a series of nested af:iterators, rather than the conventional tree or treetable. This technique exploits two features of the treebinding. First the fact that an treebinding can return both a collectionModel as well as a treeModel, this collectionModel can be used directly by an iterator. Secondly that the “rows” returned by the collectionModel themselves contain an attribute called .children. This attribute in turn gives access to a collection of all the children of that node which can also be iterated over.

Putting this together you can represent the data encoded into a tree binding in all sorts of ways.

As an example I’ve put together a very simple sample based on the HT schema and uploaded it to the ADF Sample project. It produces this UI:

Example output from this technique

The important code is shown here for a Region -> Country -> Location Hierachy:

<af:iterator id="i1" value="#{bindings.AllRegions.collectionModel}" var="rgn">
  <af:showDetailHeader text="#{rgn.RegionName}" disclosed="true" id="sdh1">
    <af:iterator id="i2" value="#{rgn.children}" var="cnty">
      <af:showDetailHeader text="#{cnty.CountryName}" disclosed="true" id="sdh2">
        <af:iterator id="i3" value="#{cnty.children}" var="loc">
          <af:panelList id="pl1">
            <af:outputText value="#{loc.City}" id="ot3"/>
          </af:panelList>
        </af:iterator>
      </af:showDetailHeader>
    </af:iterator>
  </af:showDetailHeader>
</af:iterator> 

You can download the entire sample from here:

Wednesday May 16, 2012

Placeholder Watermarks with ADF 11.1.2

I'm sure you're all familiar with the concept of watermarking an input field to provide the user with a greyed out hint as to what to do with it.  This is often used in search boxes or UIs where space is at a premium and you really don't have room for a label. Here's a small screenshot that shows what I mean:

Image of watermark in use

As you can see,  both the filter field and multi-line field have such text.  As soon as the user starts to enter values in these fields the watermark will disappear only to reappear if the user clears out the field.  In the past, there have been various JavaScript based solutions to this, but the HTML5 spec introduces a common way of doing this using an attribute on the <input> element called placeholder. Alas, only Chrome and FireFox have implemented this in their later versions, although it's on the list for IE 10. 

Now I probably won't be giving too much away if I let slip that placeholder support might possibly be standard in a future version of ADF Faces, but for now, I'm working in 11.1.2.2, so here's a simple implementation in the form of a custom JSF clientBehavior that will do it. 

For this, I actually took inspiration from a recent blog posting from Donatas Valys: Set Initial Focus component for Oracle ADF Faces.  He hit upon the smart idea of using a client behavior to mark a particular component as having initial focus, I've used essentially the same technique here, although extended a little bit because of the nature of the job I'm doing.

Create your Tag Library

So the first step will to create the tag (it will be called <afg:setWatermarkBehavior>) in your project.  Just select New > Web Tier > JSF/Facelets > Facelets Tag Library.  On the first page of the creation wizard, choose Deployable or Project Based, the choice is yours. For convenience I chose Project Based, then on Step 2 provide a file name and a namespace. I used adfglitz.taglib.xml and http://xmlns.oracle.com/adf/faces/glitz respectively, choose suitable values for your implementation. This step will do everything to register the Tag Library with the project (you'll see an entry is added to web.xml) 

Define the Behavior Definition 

Now you can edit the tag file and add the new behavior (or as I would have it "behaviour"). The taglib editor provides an overview view to make this simple:

Tag Lib editor

The important things to note here are:

  • The namespace (http://xmlns.oracle.com/adf/faces/glitz), we'll need that when adding this tag to the page. 
  • The name of the behavior tag - setWatermarkBehavior
  • The ID of the behavior tag - oracle.demo.adfglitz.watermark - I'll use this to associate an implementation class with the tag
  • The attributes.  Note that I've defined one attribute for the tag called value. I'll use this to pass the actual text that needs to be displayed in the placeholder.

You can switch to the source view of the editor and provide more information such as a friendly description of the taglib and tag, but you don't actually need to. 

Implement the Behavior

 Next we need to actually create a class that provides the implementation of the behavior. This needs to extend javax.faces.component.behavior.ClientBehaviorBase and implement javax.faces.component.StateHolder. The latter interface implementation is to ensure that the String passed in as the value of the tag is persisted and will survive re-builds of the component tree. 

The basic class definition therefore looks like this: 

@FacesBehavior("oracle.demo.adfglitz.watermark")
public class SetWatermarkBehavior extends ClientBehaviorBase implements StateHolder {...}

Note that I use the FacesBehavior annotation  to do the wiring between this implementation class and the taglib using the ID attribute defined above.

The other thing we need to implement the tag contract is the "value" that must be passed in as a  required attribute for the tag.  This is done with a simple member variable (String value;) and an associated public getter and setter pair on the class - getValue(), setValue() - standard stuff. We also have to manage the storage of this value by implementing the StateHolder interface. The code for that is not very interesting so I'll not reproduce it here, you can see it in the complete java class though.

The core method within the behavoir class is the getScript() method. This is called to encode the JavaScript  to send down with the enclosing component, however, we're going to subvert it a little - let me explain why.  If you have a clientBehavior associated with an inputItem then any script that you return from the getScript() method will be associated with a value change listener on the component.  In this case, that's not what we want, rather than applying the watermark when the value of the input changes, we want to apply it at the point in time the component is rendered. So to achieve this we actually just use and abuse the getScript() call and use the Trinidad ExtendedRenderKitService to queue up execute the JavaScript we need in a just in timely way.

Here's the implementation of the method:

//Constants used by getScript() defined at class level
private static final String SCRIPT_PREFIX = "addWatermarkBehavior(\"";
private static final String SCRIPT_ARG_SEPARATOR = "\",\""; 
private static final String SCRIPT_SUFFIX = "\");"; 
 
@Override
public String getScript(ClientBehaviorContext ctx) {
  UIComponent component = ctx.getComponent();
  //only contiune if the component is currently rendered
  if (component.isRendered()) {
    String componentType = deduceDOMElementType(component);
    //Only continue if the component is a valid type
    if (!componentType.equals(UNSUPPORTED_ELEMENT)) {
      String wmText = (getValue() == null) ? EMPTY_DEFAULT : getValue();
      StringBuilder script = new StringBuilder(SCRIPT_PREFIX);
      script.append(ctx.getSourceId());
      script.append(SCRIPT_ARG_SEPARATOR);
      script.append(componentType);
      script.append(SCRIPT_ARG_SEPARATOR);
      script.append(wmText);
      script.append(SCRIPT_SUFFIX);

      //We don't have an init event, just valueChange so push the code this way
      FacesContext fctx = ctx.getFacesContext();
      ExtendedRenderKitService extendedRenderKitService =
               Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
      extendedRenderKitService.addScript(fctx, script.toString());
    }
  }
  //And return nothing as we don't need a valuechangeListener
  return "";
}

Things to note here.  We could of course encode the entire JavaScript  function within the script string generated above. However, given that in my case, I have several uses of the behavior in the app it makes sense to shove the detail of that code into a common .js file. I already have this available on the page and call a simple function - addWatermarkBehavior(), passing the relevant component ID, type and placeholder value.  That JavaScript can be seen below.

Another point is that getScript() uses the convenience method deduceDOMElementType() which, from the component and its attributes works out: First of all if it's a valid component on which to do anything, and secondly if the placeholder will need to be set on an html <input> element or an <textarea>.

//Constants used by deduceDOMElementType() defined at class level
private static final String UNSUPPORTED_ELEMENT = "unsupported";
private static final String TEXTAREA_ELEMENT = "textarea";
private static final String INPUT_ELEMENT = "input";

private String deduceDOMElementType(UIComponent component) {
  String componentType = UNSUPPORTED_ELEMENT;
  //work out the correct component type
  if (component instanceof RichInputText) {
    //In this case we may have a multi-line item but assume intially that this is not the case
    componentType = INPUT_ELEMENT;
    //Now check for the rows attribute to see if this is multi-line
    Map<String, Object> compAttr = component.getAttributes();
    if (compAttr.containsKey("rows")) {
      int rows = (Integer)compAttr.get("rows");
      if (rows > 1) {
         componentType = TEXTAREA_ELEMENT;
      }
    }
  } else if (component instanceof RichInputDate || 
             component instanceof RichInputListOfValues ||
             component instanceof RichInputComboboxListOfValues) {
    //These all resolve to inputs at the DOM level
    componentType = INPUT_ELEMENT;
  }
  return componentType;
}

The JavaScript

 As I mentioned above, rather than stream down reams of script for each component I have a standard JavaScript file attached to my page using the <af:resource> tag and in that I've implemented the simple function to locate the correct component on the page and apply the correct placeholder text. You could also use this method as a place to add a script based solution to browsers that don't support placeholder. Here's the  function:

function addWatermarkBehavior(componentId, type, text){
    var sourceInput = AdfPage.PAGE.findComponent(componentId);
    var domElemement = AdfAgent.AGENT.getElementById(sourceInput.getClientId());
    var targetArray = domElemement.getElementsByTagName(type);
    targetArray[0].placeholder = text;
}

As you can see, pretty short and sweet, you could of course add various validations to check you have a real element  etc. but let's keep it simple.

Using the Behavior

So that's about it. The final point is, of course how to use this. Well all you need to do is register the namespace in your page or fragment as I've done here using the afg: prefix:

<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" 
                xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
                xmlns:afg="http://xmlns.oracle.com/adf/faces/glitz"
                xmlns:f="http://java.sun.com/jsf/core"> 

And then use the tag thus:

<af:inputText ...>
  <afg:setWatermarkBehaviour value="Filter Items"/> 
</af:inputText> 

 Enjoy.

Thursday Apr 05, 2012

The UIManager Pattern

One of the most common mistakes that I see when reviewing ADF application code, is the sin of storing UI component references, most commonly things like table or tree components in Session or PageFlow scope. The reasons why this is bad are simple; firstly, these UI object references are not serializable so would not survive a session migration between servers and secondly there is no guarantee that the framework will re-use the same component tree from request to request, although in practice it generally does do so.

So there danger here is, that at best you end up with an NPE after you session has migrated, and at worse, you end up pinning old generations of the component tree happily eating up your precious memory. So that's clear, we should never. ever, be storing references to components anywhere other than request scope (or maybe backing bean scope). So double check the scope of those binding attributes that map component references into a managed bean in your applications. 

Why is it Such a Common Mistake? 

At this point I want to examine why there is this urge to hold onto these references anyway? After all, JSF will obligingly populate your backing beans with the fresh and correct reference when needed.  

In most cases, it seems that the rational is down to a lack of distinction within the application between what is data and what is presentation. I think perhaps, a cause of this is the logical separation between business data behind the ADF data binding (#{bindings}) façade and the UI components themselves. Developers tend to think, OK this is my data layer behind the bindings object and everything else is just UI.  Of course that's not the case.  The UI layer itself will have state which is intrinsically linked to the UI presentation rather than the business model, but at the same time should not be tighly bound to a specific instance of any single UI component. So here's the problem.  I think developers try and use the UI components as state-holders for this kind of data, rather than using them to represent that state. An example of this might be something like the selection state of a tabset (panelTabbed), you might be interested in knowing what the currently disclosed tab is. The temptation that leads to the component reference sin is to go and ask the tabset what the selection is.  That of course is fine in context - e.g. a handler within the same request scoped bean that's got the binding to the tabset. However, it leads to problems when you subsequently want the same information outside of the immediate scope.  The simple solution seems to be to chuck that component reference into session scope and then you can simply re-check in the same way, leading of course to this mistake.

Turn it on its Head 

So the correct solution to this is to turn the problem on its head. If you are going to be interested in the value or state of some component outside of the immediate request context then it becomes persistent state (persistent in the sense that it extends beyond the lifespan of a single request). So you need to externalize that state outside of the component and have the component reference and manipulate that state as needed rather than owning it. This is what I call the UIManager pattern. 

Defining the Pattern

The  UIManager pattern really is very simple. The premise is that every application should define a session scoped managed bean, appropriately named UIManger, which is specifically responsible for holding this persistent UI component related state.  The actual makeup of the UIManger class varies depending on a needs of the application and the amount of state that needs to be stored. Generally I'll start off with a Map in which individual flags can be created as required, although you could opt for a more formal set of typed member variables with getters and setters, or indeed a mix. This UIManager class is defined as a session scoped managed bean (#{uiManager}) in the faces-config.xml. 

The pattern is to then inject this instance of the class into any other managed bean (usually request scope) that needs it using a managed property.  So typically you'll have something like this:

  <managed-bean>
    <managed-bean-name>uiManager</managed-bean-name>
    <managed-bean-class>oracle.demo.view.state.UIManager</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean> 

When is then injected into any backing bean that needs it: 

   <managed-bean>
    <managed-bean-name>mainPageBB</managed-bean-name>
    <managed-bean-class>oracle.demo.view.MainBacking</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
      <property-name>uiManager</property-name>
      <property-class>oracle.demo.view.state.UIManager</property-class>
      <value>#{uiManager}</value>
    </managed-property>
  </managed-bean>

In this case the backing bean in question needs a member variable to hold and reference the UIManager:

private UIManager _uiManager; 

Which should be exposed via a getter and setter pair with names that match the managed property name (e.g. setUiManager(UIManager _uiManager), getUiManager()). 

This will then give your code within the backing bean full access to the UI state.

UI components in the page can, of course, directly reference the uiManager bean in their properties, for example, going back to the tab-set example you might have something like this:

<af:paneltabbed>
  <af:showDetailItem text="First"
                     disclosed="#{uiManager.settings['MAIN_TABSET_STATE'].['FIRST']}">
    ...
  </af:showDetailItem>
  <af:showDetailItem text="Second"
                     disclosed="#{uiManager.settings['MAIN_TABSET_STATE'].['SECOND']}">
    ...
  </af:showDetailItem>
  ...
</af:panelTabbed>

Where in this case the settings member within the UI Manger is a Map which contains a Map of Booleans for each tab under the MAIN_TABSET_STATE key. (Just an example you could choose to store just an identifier for the selected tab or whatever, how you choose to store the state within UI Manger is up to you.)

Get into the Habit

So we can see that the UIManager pattern is not great strain to implement for an application and can even be retrofitted to an existing application with ease. The point is, however, that you should always take this approach rather than committing the sin of persistent component references which will bite you in the future or shotgun scattered UI flags on the session which are hard to maintain.  If you take the approach of always accessing all UI state via the uiManager, or perhaps a pageScope focused variant of it, you'll find your applications much easier to understand and maintain. Do it today!

More Information

Another interesting article relating to this topic has been written by Steven Davelaar  subsequent to the original publication of this post. This article is well worth checking out more more information on this area and some best practice around component references.

Thursday Mar 01, 2012

uncommittedDataWarning - It's a Matter of Timing

An interesting nugget came across my desk yesterday that I though was worth sharing. What's wrong with this picture:

Task flow Diagram

Absolutely nothing right?  That's exactly the kind of taskflow we would expect to see where the user can, from the listEmployee screen, either create a new empty record by following the new navigation rule or edit the current row using the edit rule. The problem in this particular case was, however, that the employeeList screen has uncommittedDataWarning="on" set as an attribute on the document component:

<af:document title="Employee List" 
             uncommittedDataWarning="on"> 

So what happens? Every time the new navigation rule is followed to navigate to the editEmployee screen the uncommitted data warning pops up.  So what's going on here? The listEmployee screen is read only in this case how could it have marked the data control as dirty? 

Well it all comes down to the timing of when the uncommittedDataWarning takes place. The check will be made when the JSF View ID is being changed.  If you look at the above taskflow you'll see that the createInsert method binding will take place before the navigation away from the page, so at the point in time that the continue navigation is invoked the DataControl will be dirty after all and the uncommittedDataWarning check will fail, popping up the dialog. 

The solution in this case is simple (assuming that you have to keep the uncommittedDataWarning="on" on the source page). You can revert to the technique that we used to use in ADF before TaskFlows came along.  You create the method binding for the createInsert operation in the employeeEdit Screen and add an invokeAction in the executables section of the pageDef which is conditional on a flag set on the request. Something like this:

<invokeAction id="InvokeCreateIfRequired" 
              Binds="CreateInsert" 
              RefreshCondition="#{requestScope.editAction eq 'INSERT'}"
              Refresh="ifNeeded"/> 

Then the command item that triggers the navigation to the edit page sets that flag (or does not set it if you want to do an edit rather than an insert) :

<af:commandButton text="New" id="cb1" action="new"> 
   <af:setPropertyListener from="#{'INSERT'}" 
                           to="#{requestScope.editAction}" 
                           type="action"/>
</af:commandButton>

So there you go - there is life in the old techniques yet! 

Wednesday Feb 29, 2012

CSS Animations - The Panel Flip

In the my final (for now) discussion on CSS3 animations with ADF Faces Rich Client  UI I wanted to discuss the most complex, but fundamentally useful (from a technique perspective) of the three animations shown in the introduction posting. The first two animations that I showcased were simply triggered by the browser's inherent mouse-over functionality. I didn't actually need to do anything to make the animation happen apart from assigning the correct styleClass to the panel or button.  In this final animation I'm triggering the animation from code and what's more I'm actually chaining together two animations with a server side call made in the middle. To break it down here's the logical flow:

Initiate a rotation of 90 degrees --> Change the contents of the panel -->  Return rotation to 0 degrees.

So the question is, how do we chain all of these actions together, given that the initial rotation will itself take a certain amount of time (2 seconds)?  Well, to handle this we have to write a little bit of JavaScript  that will first of all set the styleClass to provide the first 90deg animation and also register a listener function which will execute once the rotation is finished.  This callback function then executes the Server code we need and then kicks off the reverse animation. 

The Animations  

So first of all, the two styles that define the animations. This should be fairly obvious and understandable by now:

.rotateOut {
    -webkit-transition-property: -webkit-transform;
    -webkit-transform: rotateY(90deg);
    -webkit-transition-timing-function: ease-in-out;
    -webkit-transition-duration: 2s;
)
.rotateReturn {
    -webkit-transition-property: -webkit-transform;
    -webkit-transform: rotateY(0deg);
    -webkit-transition-timing-function: ease-in-out;
    -webkit-transition-duration: 2s;
}

The JSF components

In this example I'm using a switcher which is controlled by a managed bean  called uiManager. This switcher will either display the panelHeader that contains the inputForm or the comments screen. Contrary to your expectation though, it's not these panels managed by the switcher that we'll actually be animating, but rather a parent of the switcher, another panelGroupLayout. So the hierarchy looks a  like this:

  1. Panel group layout that will be animated
  2. Decorative box that will apparently rotate  
  3. Panel group layout that will be refreshed to show the change in contents
  4. Switcher 
<af:panelGroupLayout layout="vertical" 
                     id="flip" 
                     clientComponent="true"
                     binding="#{animationBB.flipPanel}" 
                     styleClass="rotateReturn">
<af:decorativeBox theme="dark">
  <f:facet name="center">
      <af:panelGroupLayout id="refresh" 
                           layout="vertical" 
                           binding="#{animationBB.refreshPanel}">
        <af:switcher defaultFacet="ASide" 
                     facetName="#{uiManager.flipAnimationSelectedSide}" >
          <f:facet name="ASide">
            <af:panelHeader text="Basic Information"> 
              ...
            </af:panelHeader> 
          </f:facet> 
          <f:facet name="BSide"> 
            <af:panelHeader text="Comments">
              ...
            </af:panelHeader> 
          </f:facet> 
        </af:switcher> 
      </af:panelGroupLayout>
    </f:facet> 
  </af:decorativeBox>
</af:panelGroupLayout>  

Notice that the outer panelGroupLayout id="flip" has a binding to a backing bean for the page, this is so we can get the correct clientId back for it (look back at my Visual Notifications During Long Running Transactions article for details of this technique). The inner pgl, id="refresh", also has a binding to the backing bean which we will use later in the serverListener to force a refresh on that panel and the switcher inside it.

 Within each of the switcher "panels" is a button (not shown above) that will actually trigger the animation.  Here's the code for the button on the A-Side:

<af:commandButton text="More..." 
                  partialSubmit="true"
                  clientComponent="true" 
                  id="cbflip1">
  <af:clientAttribute name="flipPanel"
                      value="#{animationBB.flipClientId}"/>
  <af:clientListener method="animateFlipPanel"
                     type="action"/>
  <af:serverListener type="flipEvent"
                     method="#{animationBB.handlePanelFipEvent}"/>
</af:commandButton>

The code for the B-Side button is identical. To break that down we have three things of interest:

  1. A clientAttribute that is used to send the client id of the panel that we want to spin to JavaScript
  2. A clientListener to actually wire up a JavaScript  event to the button press
  3. A serverListener which identifies the server side method that will be called half way through the animation to tell the switcher to change contents.

So let's look at each of those:

The Client Attribute

The client attribute is bound to the page backing bean where there is a simple function that asks the panel that will be "flipped" for it's client ID, in this case the flipPanel variable contains the reference to the RichPanelGroupLayout which is bound to that outer panel:

/**
 * Gets the correct client component ID for this panel in the context in which the 
 * panel is placed. This provides a safe way of getting the client ID if the component
 * is embedded in a region or similar where the computed path may change depending on the use.
 * @return clientID for use in JavaScript 
 */
 public String  getFlipClientId() {
   return flipPanel.getClientId(FacesContext.getCurrentInstance());
}

The JavaScript

The clientListener on the button calls the animateFlipPanel method. This is stored in a .js file referenced using the <af:resource> tag in the metaContainer facet of the document:

function animateFlipPanel(event) {
    //Get the ID of the panel to flip. 
    //This has been passed in via the clientAttribute 
    var fpId = event.getSource().getProperty('flipPanel');
    //Grab a reference to this button that raised the 
    //event. we need this to register the callback against
    var raisingComponent = event.getSource();

    //First animation sends us to 90deg
    var transition = "rotateOut";
    flipPanel = AdfPage.PAGE.findComponentByAbsoluteId(fpId);
    initiateClientFlip(raisingComponent, flipPanel, transition);
} 

 And here's the method that assigns the animation:

/**
 * Kick off the animation - rotate the panel to 90 degrees which will make it  
 * disappear as it's sideways on to us.
 * Then register the callback to rotate back once the animation is finished
 * @param raisingComponent - the button that kicked things off
 * @param flipPanel - the PGL to animate (assign style to) 
 * @param transition - the style to apply
 */
function initiateClientFlip(raisingComponent, flipPanel, transition) {
  //Set the style to animate the rotation
  flipPanel.setStyleClass(transition);
    
  /* Setup a callback to reverse the animation once the transition is finished.
   * Note that we remove the transition listener once it's executed so that 
   *   it is not called by the flipBack transition as well 
   * Note also that we have to attach the listener to the underlying DOM object
   */
    
  // Get the DOM object that represents this panelGroupLayout
  var flipPanelReal = AdfAgent.AGENT.getElementById(flipPanel.getClientId());
   
  //Define the callback 
  var reverseTransition = "rotateReturn"
  var flipBackFunction = function (event) 
           {animateFlipBack(raisingComponent, flipPanel, reverseTransition); 
            event.target.removeEventListener("webkitTransitionEnd",flipBackFunction);};
    
  // Add the transition listener to queue up both the animation back  
  // and the server side change which will apparently flip the
  // contents of the panel 
  flipPanelReal.addEventListener("webkitTransitionEnd", flipBackFunction,false);
} 

So the actual animation part of the above code is trivial, we just set the new style to kick that off.  The exciting part comes in the definition of the eventListener (webkitTransitionEnd) which is set up to execute once the animation has finished. Like everything that I have covered in this short series, the code here is webkit (Chrome & Safari) specific. Similar events exist both in generic terms for the future and in specific versions for FireFox and IE.

And finally the animateFlipBack() function and a convenience method that it uses:

 /**
 * Once the initial animation is done this method is invoked to change the contents of the panel and then
 * animate back to the starting point giving the illusion of a full 180 degree flip
 * @param raisingComponent - reference to the button so we can invoke the serverListener
 * @param flipPanel - pgl to animate
 * @param reverseTransition - animation style
 */
function animateFlipBack(raisingComponent, flipPanel, reverseTransition) {
  // Call the event on the server which will cause the switcher to 
  // change it's contents and issue a PPR event
  raiseServerFlipEvent(raisingComponent);

  //Now start the return animation
  flipPanel.setStyleClass(reverseTransition);   
}

/**
 * Simple function to queue up the server side event on the button that 
 * instigated the flip. This event does not need to send a payload
 * @param raisingComponent - the button 
 */
function raiseServerFlipEvent(raisingComponent){
    var flipEvent = new AdfCustomEvent(raisingComponent,"flipEvent",{},false);
    flipEvent.queue(true);
}

The serverListener 

The role of the serverListener code is to toggle the facet that the switcher is currently displaying and then force the panel containing the switcher to refresh with a PPR event:

/**
 * Event raised from JavaScript to tell us that the flip animation is underway and we 
 * need to change the content on the panel 
 * @param clientEvent
 */
public void handlePanelFipEvent(ClientEvent clientEvent) {
  String currentSide = _uiManager.getFlipAnimationSelectedSide();
  String targetSide = UIManager.A_SIDE;
  if (currentSide.equals(UIManager.A_SIDE)){
      targetSide = UIManager.B_SIDE;
  }
  _logger.info("Flipping to " + targetSide);
  getUiManager().setFlipAnimationSelectedSide(targetSide);
        
  // And queue the client refresh
  AdfFacesContext.getCurrentInstance().addPartialTarget(getRefreshPanel());
}

 This function as you can see is a toggle based on the current value stored in the uiManager.  The UIManager itself is a session scoped bean that just holds the current side value.  It is injected into this backing bean using a managed property in the managed bean definition. The function getRefreshPanel() returns a reference to the pgl that surrounds the switcher (id="refresh" in the hierarchy above). 

Wrap Up 

The technique that I've discussed within this article may seem a little complex but in fact it's all quite logical and can be quite easily extended and made generic. You can see how more complex animations may be achieved by stringing together a whole sequence of callbacks to execute after the previous animation has completed and how you can wire in server side calls into the mix. 

Tuesday Feb 21, 2012

CSS Animations - Slide Out Panel

In this second the detailed articles on the animation prototype I'm going to look at the animation of a whole group of components at once in the form of a slide out panel menu effect. This animation combines two effects (if you look closely),  The actual slide-out, and, in parallel, a shadow around the panel.  Just like the first example, no Java or JavaScript code is required for this example, we can just use default CSS hover functionality to trigger the animation.

Again, just a warning.  This prototype was only set up for the WebKit based browsers (Safari and Chrome). So where I talk about -webkit* styles below be aware that these will have different names on FireFox (usually -moz*) and IE. A stylesheet that handles more browsers will have to include these versions as well. 

The start point for this article assumes that your style sheet (via a custom skin) already exists (go back to the first article if you've not read this yet).

However, before we get too excited about the animations we need to think a little about layout.  The last thing we want to do with these funky effects is to mess up the rest of the screen, and so at this stage, where we've not got any scripting involved, we need to make sure that space is available in the UI for the slide out. To do this I started out by laying out my components thus:

<af:panelGroupLayout (Horizontal)>
  <af:panelGroupLayout (Vertical)> - will be animated
    [Menu] 
  </af:panelGroupLayout> 
  <af:spacer .../>  
  <af:panelGroupLayout (Vertical)>
    [Decorative Box etc.] 
  </af:panelGroupLayout>  
</af:panelGroupLayout> 

So basically the panelGroupLayout that will be animated already has space "reserved" for it on the screen and, at design time, it occupies that slot. At this stage you also need to work out how wide the panel that you are animating will be once populated. This is so you can push it off screen, but still expose enough for the mouse to hover over. In my case the pgl comes out at around 100px once populated.

Hint: To keep things simple with these "container" animations I recommend that you attach effects to a vertical panelGroupLayout. In HTML terms these components get rendered as a simple html <div> which is perfect and makes things much easier to debug if the styles don't seem to be working.

On to the animation. When I started this animation I was not sure what components I would be animating so, rather than defining a specific sub-type as I did for the button in the last article, I'm defining a generic styleClass that could be used with any components. Again we need the base class(.slideout) and the :hover selector for it.

.slideout
{
}
.slideout:hover
{
} 

Again concentrating on the base selector first, let's look at the transitions. This time I'm kicking it up a notch (to coin a phrase) by desiring two concurrent animations, the slide out and the shadow.  Just like the last example with the commandButton we want to pair effects so that any effect I put on the hover needs to be mirrored and reversed by the base selector. This way, both the mouse-over and mouse-out will be smoothly animated.

So here's the base selector.  This "parks" the panel off screen with a little bit hanging out for the mouse to hover over and start the animation:

.slideout
{
  position: relative;
  -webkit-transition-property: left, -webkit-box-shadow; ;  
  -webkit-transition-timing-function: linear, ease-out;
  -webkit-transition-duration: 1s, 1s;  
  -webkit-box-shadow: 0px 0px 0px;
  left: -80px; 
} 

The story here is going to be similar to the simple button scaling discussed in part 1, but there are some differences worthy of mention.  First of all the position property. As part of this animation we're actually moving a <div> and all it's contents around the screen. I want to do that in relation to it's initial position (remember that we started out with the panel in a conventional layout). So the relative keyword is used for this. The other option here is absolute. Try setting this to absolute if you give this a go, you'll notice that the results can be, shall we say unpredictable. You need to be really careful if you're trying to move things around out of context and could end up fighting with the ADF layout algorythms. So stick to relative for now!

Next the -webkit-transition-property - this time it contains two properties, separated by a comma: left and -webkit-box-shadow. So I'm declaring here that those are the two properties I'll be animating the change in. As I'm now dealing with two properties the other -webkit-transition* properties also contain two values separated by a comma and those refer to each property transition. Thus if you look at the timing function, you'll see how the animation in the change of the left value will be linear whereas the shadow will use the ease-in-out rate of change just like the button did.  Again have a play with this and see what the difference is between the timing effects, you have total control. 

Both animations take place concurrently and take place over the same 1s duration although they could be of different durations.  In the next article I'll be looking at how animations can be queued up to provide more complex effects. 

Finally we have the endpoints for  the shadow (basically no shadow) and left. Notice with the left property, it is set to -80px. Recall that the positioning for this style is relative, so this really just tells the panel to shift 80px to the left from its starting position, which happens, in this case, to place it mostly, but not quite all, off screen.

Next the hover selector, it is, as you would expect pretty similar except that we move the pgl back to where it belongs at 0px (relative) and introduces some shadow:

.slideout:hover
{
  position: relative;
  -webkit-transition-property: left, -webkit-box-shadow;  
  -webkit-transition-timing-function: linear, ease-in;
  -webkit-transition-duration: 1s, 1s;  
  -webkit-box-shadow: 10px 10px 5px #888;
  left: 0px;
}

The final step is, of course, to assign this style to the pgl itself. Again this is done with the styleClass property 

<af:panelGroupLayout id="pglmenu" layout="vertical" styleClass="slideout"> 
... 

There you have it.  In the final article in this series I'll be investigating a more complex animation involving JavaScript and other goodies - stay tuned! 

Monday Feb 20, 2012

CSS Animations - Basic Hover Scaling

In this first of the detailed articles on the animation prototype I wanted to look at the basic setup of application and the simplest of the animations - the Pick Me! button.  The Pick Me! button animates to draw attention to itself as the mouse moves over it.  The actual animation itself is a simple scaling so it's a good place to start. 

Again, just a warning.  This prototype was only set up for the WebKit based browsers (Safari and Chrome). So where I talk about -webkit* styles below be aware that these will have different names on FireFox (usually -moz*) and IE. A stylesheet that handles more browsers will have to include these versions as well. 

The basis of all of this work is a custom skin so that's the first thing to create. In 11.1.2 this is really as simple as going to the New Gallery and creating a new skin in the project.  Here I've extended the fusionFx-simple-v2.desktop skin as I want to keep the standard look and feel.  If you've not created a skin for your applications before, there are plenty of articles out there to help you, so I won't dwell on that.

Now in the skin itself I want to create a sub-type of button which will do the animation.  I don't want to animate every button, that would probably make the users seasick.  So the first step is to create a sub-type of the button thus:

af|commandButton.zoomOnFocus
{
} 

This sub-type can then  applied to the selected buttons using the styleClass property:

<af:commandButton text="Pick Me!" styleClass="zoomOnFocus" ...>

Now to implement the  zoom behaviour, fortunately this does not involve writing any code apart from the actual CSS to provide the style. To do this we define a :hover selector for the zoomOnFocus in the skin:

af|commandButton.zoomOnFocus:hover
{
} 

Now we fill that in with the desired animation, I'll explain each of the lines next:

af|commandButton.zoomOnFocus:hover
{
  -webkit-transition-property: -webkit-transform;
  -webkit-transform: scale(2,2); 
  -webkit-transition-timing-function: ease-in-out;
  -webkit-transition-duration: 1s;  
}

The first line,  -webkit-transition-property defines which property, -webkit-transform, should be animated. The second line -webkit-transform itself defines the end result of the animation which is a scale function. This takes two arguments for the x and y scaling respectively, so scale(2,2) doubles the size of the button in both directions.

The third line  -webkit-transition-timing-function, defines the progression of the animation in time. There are various options for this, the ease-in-out function starts the animation slowly, then speeds up and then slows down again towards the end. This provides a pleasing effect to the eye.

Finally -webkit-transition-duration defines how long the animation should take.

If you do all of this, the zoom will work nicely as you move the mouse over the button, however, as you move the mouse out the style will snap back instantly, so we need to complete the recipe with an animation the other way on the base style:

af|commandButton.zoomOnFocus
{
  -webkit-transition-property: -webkit-transform;
  -webkit-transform: scale(1,1); 
  -webkit-transition-timing-function: ease-in-out;
   -webkit-transition-duration: 1s;  
}

This style is pretty much identical to the :hover version.  The only difference is the scale() factor which is changed to 1,1 to return the button to it's normal size.  With this all in place, the button will gracefully shrink back to its normal size. 

So that's the first, very simple animation, in the next article in the series I'll look at the only slightly more complex slide-out panel animation. 

Friday Feb 17, 2012

Experiments with CSS animations in ADF Applications - Introduction

We have had several ADF users experimenting with JQuery to achieve animation effects and that made me wonder how feasible it would be to work with pure CSS3 animations within the context of an ADF application.  From the point of view of the product, we're thinking about how we might express a broader range of animations in a declaritive way, but for now I wanted to see what was possible with the product as it stands.  To wet your appetite, here's a short video of my initial prototype.  I'll explain all of the effects in follow up articles - so be patient with me and, of course, keep coming back to the blog for more!

This prototype was written in 11.1.2.1, but there is nothing there that would not work in 11.1.1.n. Also I've only worked with WebKit browsers (Chrome / Safari so far),  this is still an area where the different browser families need different treatment. This will be an exercise for you the reader. 

If you have an up to date version of Chrome, Safari or IE9 you can play the following short video in-line, Firefox users, sorry I had problems with encoding to ogg format, you'll have to download:

If not download it from here (Size: 374k) 

I've used three sets of animations here:

  1. Simple animated zoom on the Pick Me! button. It grows and then shrinks as the mouse moves over and out.
  2. Panel slide-out effect with shadow for the "menu" panel.
  3. Panel flip effect where the panel rotates through 180 degrees to show some different content, the kind of effect you'll see on your phone fairly often. (I'll admit that this one is very cool)
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 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today