Posts in Category "Flex"

Controlling Navigation of Dynamically Added Modules in Cairngorm

In a recent application I was building there was a requirement for modules to be dynamically added at runtime based on a configuration file.  Since the application was already using Parsley and Cairngorm I looked to the existing Cairngorm navigation and module libraries to see if this was supported. I quickly discovered that the dynamic addition of modules and then being able to navigate to these modules was not supported out of the box. This led me to implement my own solution on top of the existing Cairngorm 3 libraries. There have also been a number of questions over on the Adobe forums about this topic so I decided it would be worth blogging about.

To solve my problem I had to implement two new distinct features.

  1. Dynamically add modules at runtime
  2. Connect these dynamically loaded modules to Cairngorm navigation

Dynamically Adding Modules

I’ll first look at how modules can be dynamically added to a Cairngorm/Parsley application based on a configuration file.

This is the simple XML file I defined that contains information about each module that needs to be loaded.

<modules>
<module id="mod1" label="Module 1" url="Module1.swf" location="content"/>
<module id="mod2" label="Module 2" url="Module2.swf" location="content"/>
</modules>

During the initialization of the main presentation model a message is dispatched to load the XML.

sendMessage(new LoadModuleConfigMessage(LoadModuleConfigMessage.LOAD));

Once the XML is loaded, a ModuleSet is updated to include a collection of Modules defined in the XML.  After the XML completes loading and is configured a second message is dispatched to add the modules to the view.  In this example all the modules are added to a ViewStack through the use of a repeater.

<mx:Repeater id="panelRepeater" dataProvider="{model.panels}"
repeatEnd="model.handleRepeatEnd(this)">
  <core:ControllerViewLoader width="100%" height="100%"
automationName="{panelRepeater.currentItem.moduleId}"
moduleManager="{panelRepeater.currentItem.moduleInfo}"
loadPolicy="{panelRepeater.currentItem.loadPolicy}"/>
</mx:Repeater>

You’ll notice that the repeater gets its data from a panels collection in its presentation model and simply repeats a ControllerViewLoader.  In the presentation model for this ViewStack there is a handler for the LoadModuleConfigMessage complete message.  In this handler the panels collection is generated by creating objects that contain the moduleId, moduleInfo and loadPolicy. The moduleInfo property is an instance of the ParsleyModuleManager which implements the IModuleManager interface.  It is this module manager that defines the location of the actual module SWF and what context and domain it should be loaded into.

[MessageHandler(selector="complete")]
public function loadComplete(msg:LoadModuleConfigMessage):void
{ 
    toolbarPM.menuItems.items.removeAll();
    ...
    moduleViews = new Array();
    var i:int;
    for (i = 0; i < modSet.modules.length; i++)
    { 
  var module:Module = modSet.modules.getItemAt(i) as Module;
       var view:Object = new Object();
       view.moduleId = module.location + "." + module.id;
       var modInfo:ParsleyModuleManager =
new ParsleyModuleManager(module.url, this.context,
                ClassInfo.currentDomain, null, null);
       view.moduleInfo = modInfo;
       view.loadPolicy = new BasicLoadPolicy(); ;
       moduleViews.push(new ObjectProxy(view));
       //Add menu bar item
       var dest:ContentDestination = new ContentDestination();
      dest.label = module.label;
      dest.destination = view.moduleId; 
      toolbarPM.menuItems.addDestination(dest);
    }
}
panels = new ArrayCollection(moduleViews);
}

Connecting Dynamically Added Modules to Navigation

A [Waypoint] metadata tag was first added to the ViewStack as specified in the Cairngorm documentation.

After the repeatEnd event is dispatched by the repeater in the ViewStack the modules have all been added and can now be safely wired to Cairngorm navigation.  This can be achieved by dispatching the Initialize event of the view stack.

vs.dispatchEvent(new Event(FlexEvent.INITIALIZE));

In order to actually navigate to a module the module has to be loaded first and then navigated to.  The module only needs to be loaded the first time it is navigated to.

 dispatchMessage(new LoadModuleMessage(destination));
 dispatchEvent(NavigationEvent.createNavigateToEvent(destination));

Finally, when defining landmarks in each individual module the landmark id must match the module id that was defined.  In the case of my solution moduleId is created from the location and id attributes defined in moduleConfig.xml.

Feel free to check out the full solution form the link below.

Share on Facebook

Firing form guide events from your form design script

When designing forms in LiveCycle Designer ES that will eventually be rendered as a form guide by the server I regularly come across the need for script on the form to notify the form guide something needs to be done.  One such case is the use of xfa.host.messageBox().  When this script is executed in a form guide the default Flex Alert is shown.  While this is fine for quick informational messages it cannot be styled to match the look of my form guide nor can it handle any user response.  This article covers one possible technique that can be used to allow your form scripting to send events to the form guide.

 

Step 1: xfa.host.name == "Flash"

This one line of script will very quickly become your friend when scripting a form that may be rendered as a form guide.  Keying on the current host name allows you to target your script to only run in certain environments.  This technique is not only valuable to ensure scripting outside of the supported xfa subset can still be executed in Acrobat/Reader it also opens the door for some inventive scripting when the form is being hosted in Flash (ie. form guide).  One such door that opens is the ability to enter ActionScript directly in your form design script that will only get executed when in a form guide context.

 

Step 2: Add ActionScript to your form design script

Let’s look at a case where the client-side click event of a button in your form design has the following script.

//Show Yes/No message box
var ans = xfa.host.messageBox("My Text" , "My Title",  2);

if (ans == 3)
{
   // No is selected
}
else
{
   // Yes is selected
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

In order to get a similar result in the form guide where the user gets presented with a Yes/No message box that must be answered an event can be fired. On the same click event we can add the following script.

if (xfa.host.name == "Flash")
{
this.dispatchEvent(new XfaModelEvent("showCustomDialog", false, false, "My title", "My text", null);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This ActionScript will fire an event on the form guide that can be listened for and acted upon accordingly.  The name of the event, showCustomDialog, could be any string you wish.  You could also fire events with multiple names fro myour form script for any number of desired behaviours.

Step 3: Create a Popup Manager in Flex

In order for this technique to work your form guide will need to reference a custom Flex library that includes a custom guide layout (ie. Wrapper).

First, I’ll explore the creation of a Xfa Popup Manager in Flex. There are many ways you could write your Flex code to display popups requested from scripting in your form design.  This method is one such approach but could be altered to suit your individual needs.  Here are the basic steps I will be following.

  1. Create a class called XfaPopUpManager
  2. Create a static public function called "manage" that takes a Page as input
  3. Set up listeners for all panel items on the current page
  4. Handle popup events when they occur
  5. Listen for popup close events

And here is the abbreviated class code.

public class XfaPopUpManager
{
private var popup:Alert;

static public function manage (oPage:Page):XfaPopUpManager
{
return new XfaPopUpManager(oPage);
}

public function XfaPopUpManager(oPage:Page):void
{
addPopupListeners(oPage);
}

/**
      * Creates listeners in order to provide a custom popup dialog for
      * those fields that dispatch a "showCustomDialog" XfaModelEvent at runtime.
      */
public function addPopupListeners(oPage:Page):void
{
for each (var item:PanelItem in oPage.layout.getPanelItems())
{
if(item.boundData != null && item.boundData.hasEventListener("showCustomDialog") == false)
{
item.boundData.addEventListener("showCustomDialog", popupHandler);
}
}
}




    /**    
* show popup has been requested - present a dialog.
*/
protected function popupHandler(oEvent:Event):void
{
if (displayPanelItems[oEvent.currentTarget] != null)
{
var title:String = "";
var msg:String = "";
if (oEvent is XfaModelEvent)
{
title = XfaModelEvent(oEvent).propertyName;
msg = XfaModelEvent(oEvent).propertyValue;
}
doShowPopup(title, msg);
}
}
    /** 
* Creates Dialog style popup for programmed popUp requests.
* @param title
* @param displayText
* /
protected function doShowPopup(title:String, displayText:String):void
{
// Prevent multiple occurrences of a modal dialog. This may happen due to cloned
// fields that already have listeners.
if (popup != null) return;
var flags:uint = Alert.YES | Alert.NO;
popup = Alert.show(displayExt, title, flags, null, handlePopupClose);
}
    /** 
* Handle the pop up close event
*/

private function handlePopupClose(event:CloseEvent):void
{
if (event.detail == Alert.YES)
{
//Yes selected
} else if (event.detail == alert.NO)
{
//No selected
}
popup = null;
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

In the handlePopupClose function you could place any logic required by the form. An extension to this technique that I won’t cover here would be to pass the SOM expression of a hidden button on your form that could be programmatically clicked as another parameter of the XfaModelEvent.  This way the script that runs after the message box is dismissed could be the same whether the form is being hosted by Acrobat or Flash.

For example…

Xfa.instance.resolveNode(somExpression).execEvent("click");

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

Step 3: Hook it all up  in your custom guide layout

We’re almost done!  All that needs to be done now is some wiring in your custom guide layout.

First you will need to listen for the Page Selection Change event.

panelManager.addEventListener(GAEvent.PAGE_SELECTION_CHANGE, pageChange);

This listener can be added to your wrapper’s createChildren() function.

Next the page change listener function needs to be created which is where the popup manager we created will be set up.

private function pageChange(event:GAEvent):void
{
//...
XfaPopUpManager.manage(event.page);
//...
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

When running a a form guide your form should now be able to dispatch events to the guide runtime and then act on those events based on the custom Flex code you tied those events to.

Share on Facebook

Keeping your custom guide layouts styled: Part 1

When creating custom layouts to be used with form guides it is a good practice to ensure that your layouts can be styled.  One method that will achieve this goal is to use the style names that get defined by Guide Builder.  By adding these style names to various components in your layout you can take advantage of all the styling that gets defined in Guide Builder and is included with each guide definition.

Here are a selection of style names that can be added to your custom guide layouts.  These styles can be defined by going to Customize Appearance in Guide Builder and navigating to the appropriate panel listed under each style name description.

Style Name MXML Description
application

<mx:VBox width="100%" height="100%" styleName="application"/>

Defines the overall background gradient colors for the guide.

Guide Builder: Customize guide, logo and progress bar -> Background

logo <mx:VBox styleName="logo" left="0"/> Specifies a location to hold an image such as a logo.

Guide Builder: Customize guide, logo and progress bar -> Logo

guidetitle <mx:Label text="{panelManager.gaModel.name}" styleName="guidetitle"/> Specifies font and color styles.

Guide Builder: Customize guide, logo and progress bar -> Font

guide

<mx:HDividedBox styleName="guide"/>

Defines background color/image, border color, padding and corner radius styles.

Guide Builder: Guide Builder: Customize guide, logo and progress bar -> Border, Guide background

panelnav

<mx:Panel styleName="panelnav"/>

Defines font, background color, background image, background alpha, border and header styles.

Guide Builder: Customize panels -> Panel border, Panel header, Navigation, Font

guidehelp

<mx:Panel styleName="guidehelp"/>

Defines font, background color, background image, background alpha, border and header styles.

Guide Builder: Customize panels -> Panel border, Panel header, Guide help, Font

paneldata

<mx:Panel styleName="paneldata"/>

Defines font, background color, background image, background alpha, border and header styles.

Guide Builder: Customize panels -> Panel border, Panel header, Data entry, Font

buttons

<gc:NextPanelButton styleName="buttons"/>

Defines theme color, fill colors and font styles.

Guide Builder: Customize buttons and repeater layouts -> Button color, Button theme color, Font

 

For further information on how to include form guide specific style names in your own layouts browse the guide source included with the LiveCycle install.  The source and associated Flex project have been made available for all the default guide layouts, panel layouts and guide controls.

./LiveCycle8.2/LiveCycle_ES_SDK/samples/FormGuides/GuideSource

In my next post I will cover the various styles that can be applied to your custom panel layouts.

Share on Facebook

Guided self-service applications

Guided self-service applications allow customers to access information and perform routine tasks without requiring any interaction with a representative of an enterprise. They can also reduce operational costs through decreased paperwork and lower call center activity. Users can be given relevant information in the context of the transactions they are trying to complete and even have real-time collaboration with a representative when required. Examples of guided self-service applications are product configurators, online banking and e-commerce.

Rich Internet applications (RIA), such as those created using Adobe Flex 2.0, are an ideal candidate for creating a guided self-service application for the web.

Adobe Flex 2.0 can simplify complex, multi-step processes in a single, dynamic interface that provides a higher level of service to customers, resulting in deeper loyalty and higher-value relationships. An RIA built using Adobe Flex can also provide in-context prompting through audio and video integration providing the real-time collaboration required by self-service applications.

While a guided self-service application is an excellent means for effectively capturing data and providing a superior user experience, a complete Enterprise solution will also require the ability to archive signed documents, provide output that can be printed and offer the ability to go offline. An RIA cannot satisfy all of these requirements on its own but it should be able to integrate easily with additional server solutions in order to gain this additional functionality.

Adobe LiveCycle products are able to produce documents that can be signed, printed, archived and filled offline by dynamically generating PDFs that can be viewed using the Adobe Reader.

A RIA created using Adobe Flex and an application created using Adobe LiveCycle can then be integrated into one complete solution.

Want to learn more? Attend Adobe MAX during October 24 – 26 in Las Vegas and be sure to check out some of the Adobe LiveCycle sessions.  In particular there is an Adobe LiveCylce Forms workshop that will show how to create and integrate a guided selef-service application using Adobe Flex and Adobe LiveCycle Forms.

technorati tags:, , ,

Blogged with Flock

Share on Facebook