Pages

Wednesday, March 19, 2008

Dynamic Adobe Flex Menu based on the Taskflow menu xml

In this blog I will show you how you can use Adobe Flex to create a flash menu which you can use in your JDeveloper 11g taskflow application. You only have to add this flash component to the jsf template. The Flash menu read the taskflow menu (root_menu.xml) and the adfc-config.xml file and generates the menu. You can the change the menu xml and the flash menu adapts to it.
To get this working I need to read the adfc-config.xml too to know which url is connected with an action.
How you can make a taskflow menu in JDeveloper 11g see my previous blog

With Flex you are very flexible to design you own menu or tabs. There are a lot of examples how you can archieve this. In this blog I made two menu's . The first is a menubar (horizontal ) and the second is a vertical menu.
Here are some pics but you can make much shinier and glamorous. In my case I used different icons for group and menuitems. Here you see the menubar

This is the vertical menu

This is how it default looks with JDeveloper. With Flex you make tabs yourself with all the special features like an icon in the tab , hoovering , transparancy etc.

This is an example of the root_menu.xml generated with JDeveloper and I changed it a little bit to make it more complex.

<?xml version="1.0" encoding="windows-1252" ?>
<menu xmlns="http://myfaces.apache.org/trinidad/menu">
<groupNode id="gn1" idref="itemNode_page1" label="menu1">
<itemNode id="itemNode_page1" label="label_page1" action="adfMenu_page1" focusViewId="/page1"/>
</groupNode>
<groupNode id="gn2" idref="itemNode_page2" label="menu2">
<itemNode id="itemNode_page2" label="label_page2" action="adfMenu_page2" focusViewId="/page2"/>
<itemNode id="itemNode_page3" label="label_page3" action="adfMenu_page3" focusViewId="/page3"/>
<groupNode id="gn3" idref="itemNode_page4" label="menu3">
<itemNode id="itemNode_page4" label="label_page4" action="adfMenu_page4" focusViewId="/page4"/>
</groupNode>
</groupNode>
</menu>

In the JDeveloper project I had to make two servlets which returns the root_menu.xml and the adfc-config.xml to the flash component because these file are located in the WEB-INF where I can't download them.

Now we can make a new project in Flex Builder 3. The result swf we have to put in the ViewController\public_html folder. Now we can add the flash object to the jsf template

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="jdeveloper_flex_menu" width="400" height="175"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="jdeveloper_flex_menu.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#869ca7" />
<param name="allowScriptAccess" value="always" />
</object>


These are the steps I did in flex. The first step is to read the xml's and generate a new flex menu xml. For every menuitem in the root_menu.xml I had to read the adfc-config to find the real url of this menuitem. The last step is to generate the menu's with the new menu xml. I also added an event to the menuitems so I know when an user a menuitem cliks so I can redirect them to the right place.

The flex mxml looks like this.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" creationComplete="init();" width="400" height="175">

<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.controls.Menu;
import mx.events.MenuEvent;
import mx.collections.*;
import flash.xml.XMLDocument;
import flash.xml.XMLNode;
import flash.xml.XMLNodeType;

[Bindable]
private var myXml:XML;
private var myXml2:XML;
private var rootMenuXml:XML;
private var adfcconfigXml:XML;
private var afdcconfig:XMLDocument = new XMLDocument();
private var weburl:String = "http://xpcnd7010xmp:8989/flex_menu-ViewController-context-root/";

[Bindable]
public var menuBarCollection:XMLListCollection;
[Bindable]
public var weburlMenu:String = weburl+"menu";
[Bindable]
public var weburlAdfc:String = weburl+"adfc";


private function resultHandler(event:ResultEvent):void {
rootMenuXml = event.result as XML;
trace(rootMenuXml.toString());
}
private function resultHandler2(event:ResultEvent):void {
adfcconfigXml = event.result as XML;
trace(adfcconfigXml.toString());
extractMenu();
}


public function init():void{
service.send();
service2.send();
}


// main menu icon
[Embed(source="assets/main.gif")]
public var main:Class;

// menuitem icon
[Embed(source="assets/item.gif")]
public var item:Class;

// parse menu
private function parseMenuXML(node:XMLNode):XMLDocument {
var mainXML:XMLDocument = new XMLDocument();
var rootXML:XMLNode = mainXML.createElement("root");

var menuItems:Array = node.childNodes;
for each(var item:XMLNode in menuItems) {
parseMenuItemXML(item, mainXML,rootXML);
}
mainXML.appendChild(rootXML);
return mainXML;
}
// parse menu items
private function parseMenuItemXML(node:XMLNode, mainXML:XMLDocument, element:XMLNode):void {
var elementXML:XMLNode = mainXML.createElement("menuitem");
elementXML.attributes.label = node.attributes.label;


if (node.nodeName=="groupNode" ) {
elementXML.attributes.icon = "main";
} else {

trace(node.attributes.label + " " +node.attributes.action);
elementXML.attributes.icon = "item";

var action:String = findAction(node.attributes.action,afdcconfig)
if (action!="empty" ) {
var url:String = findActionUrl(action,afdcconfig)
if (url!="empty" ) {
elementXML.attributes.url = url;
}
}
}
element.appendChild(elementXML);


var menuItems:Array = node.childNodes;
for each(var item:XMLNode in menuItems) {
parseMenuItemXML(item, mainXML, elementXML);
}
}


// find the action so we can use this to find the page url
private function findAction(action:String, afdc:XMLDocument):String {
var menuItems:Array = afdc.childNodes;
for each(var item:XMLNode in menuItems) {
if (item.hasChildNodes()) {
var menuItems2:Array = item.childNodes;
for each(var item2:XMLNode in menuItems2) {
if ( item2.nodeName == "control-flow-rule" ){
var menuItems3:Array = item2.childNodes;
for each(var item3:XMLNode in menuItems3) {
if ( item3.nodeName == "control-flow-case" ){
var menuItems4:Array = item3.childNodes;
var item4:XMLNode = menuItems4[0];
var item5:XMLNode = menuItems4[1];
if ( item4.firstChild.nodeValue == action ) {
return item5.firstChild.nodeValue
}
}
}
}
}
}
}
return "empty";
}

// find the url of the page
private function findActionUrl(action:String, afdc:XMLDocument):String {
var menuItems:Array = afdc.childNodes;
for each(var item:XMLNode in menuItems) {
if (item.hasChildNodes()) {
var menuItems2:Array = item.childNodes;
for each(var item2:XMLNode in menuItems2) {
if ( item2.nodeName == "view" ){
if ( item2.attributes.id == action ) {
return item2.firstChild.firstChild.nodeValue
}
}
}
}
}
return "empty";
}
// main function
private function extractMenu():void {

// load the menu xml
var result:XMLDocument = new XMLDocument();
result.ignoreWhite = true;
result.parseXML(rootMenuXml);

// load action xml
afdcconfig.ignoreWhite = true;
afdcconfig.parseXML(adfcconfigXml);

// parse the menu xml and find the action url's
var result2:XMLDocument = parseMenuXML(result.firstChild);
trace(result2.toString());
myXml = new XML(result2);

// prepare the xml for the vertical menu
menuBarCollection = new XMLListCollection(myXml.children());

// show the vertical menu
createAndShow();
}

private function createAndShow():void {
var myMenu:Menu = Menu.createMenu(null, myXml, false);
myMenu.labelField="@label";
myMenu.styleName="menubase"
myMenu.iconField="@icon";
myMenu.show(250, 0);
myMenu.rowHeight=35;
myMenu.addEventListener("itemClick", menuHandler);
myMenu.useHandCursor=true
myMenu.buttonMode=true
}

// Event handler for the Menu control's change event.
private function menuHandler(event:MenuEvent):void {
trace("URL to open::"+event.item.@url);
var urlReq:URLRequest = new URLRequest(weburl+"faces"+event.item.@url);
navigateToURL(urlReq, "_self");
}


]]>
</mx:Script>

<mx:HTTPService id="service" url="{weburlMenu}"
result="resultHandler(event)" resultFormat="e4x" showBusyCursor="true" />

<mx:HTTPService id="service2" url="{weburlAdfc}"
result="resultHandler2(event)" resultFormat="e4x" showBusyCursor="true" />

<mx:MenuBar id="bar" dataProvider="{menuBarCollection}"
iconField="@icon" labelField="@label" x="0" y="0"
useHandCursor="true" buttonMode="true"
itemClick="menuHandler(event)" />
</mx:Application>


Download the jdeveloper 11g project here and here is the source code of flex
To make it work on your on pc change the weburl in the flex source and change the path of the root_menu.xml and adfc-config in the two servlets and it should work

No comments:

Post a Comment