The AF – Client Manager Model


we set up the Cairngorm configuration for Flex by creating the base classes for the delegates, model locator, services, and controller. We also instantiated the controller and services as singletons in the <mx:Application> MXML.


To properly wire the application, we need to use the model for binding <mx:ViewStack selectedIndex="{model.MainViewStackState}">,

ModelLocator.mxml


package com.af.clientmanager.model
{
import com.adobe.cairngorm.model.ModelLocator;
import com.af.clientmanager.model.vo.*;
import mx.collections.ArrayCollection;
[Bindable]
public class ModelLocator implements com.adobe.cairngorm.model.ModelLocator
{
private static var modelLocator :
com.af.clientmanager.model.ModelLocator;
public static function getInstance() :
com.af.clientmanager.model.ModelLocator
{
if ( modelLocator == null )
modelLocator = new com.af.clientmanager.model.ModelLocator();
return modelLocator;
}
public function ModelLocator()
{
if(
com.af.clientmanager.model.ModelLocator.modelLocator !=
null )
throw new Error( "Only one ModelLocator" +
"instance should be instantiated" );
}
public const SERVICE_ENDPOINT:String =
"http://localhost:8080/af_Central/spring/messagebroker/amf";
public const YAHOO_APP_ID:String = "YOUR YAHOO APP ID";
public const UPLOAD_URL:String = "";
public const DIRECTORY_NAME:String = "af_Central";
//Global Menu Constants
public const CLIENT_MANAGER:int = 0;
public const PROJECT_ADMIN:int = 1;
public const DASHBOARD:int = 2;
public var MainViewStackState:int = 0;

// Data Providers
public var clientsDP:ArrayCollection = new ArrayCollection();
public var contactsDP:ArrayCollection = new ArrayCollection();
public var issuesDP:ArrayCollection = new ArrayCollection();
public var documentsDP:ArrayCollection = new ArrayCollection();
public var selectedClient:ClientVO = new ClientVO;
public var selectedContact:ClientContactsVO = new ClientContactsVO;
}
}

We also need to build the base value object for the client object

ClientVO.as


package com.af.clientmanager.model.vo
{
[Bindable]
[RemoteClass(alias="com.af.core.domain.Client")]
public class ClientVO
{
public var objectIdentifier:Number;
public var assocobjectID:Number;
public var clientName:String;
public var clientID:int;
public var link:String;
public var description:String;
public var notes:String;
public var phone:String;
public var addressLine1:String;
public var addressLine2:String;
public var city:String;
public var state:String;
public var zip:String;
}
}

AF – Client Manager Application Wiring


Application View We’ll start with the top of the component model: af_ClientManager.mxml. This contains the <mx:Application> tag

af_ClientManager.mxml



<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
pageTitle="AF Client Manager"
layout="absolute"
xmlns:view="com.af.view.*"
xmlns:services="com.af.clientmanager.services.*"
xmlns:control="com.af.clientmanager.control.*"
xmlns:Main="com.af.clientmanager.view.Main.*"
xmlns:model="com.af.clientmanager.model.*"
initialize="initApp()" >
<mx:Style source="assets/css/style.css"/>
<mx:Script>
<![CDATA[
import mx.events.ListEvent;
import mx.controls.Alert;
private function initApp():void
{
// Currently set to applicationView to bypass
// the login process.
vsAppLevel.selectedChild = applicationView;
}
public function loginSuccess():void
{
vsAppLevel.selectedChild = applicationView;
}
]]>
</mx:Script>

<!-- ===================================================================== -->
<!-- the ServiceLocator where we specify the remote services -->
<services:Services id="services"/>
<!-- the FrontController, containing Commands-to-Event mappings -->
<control:MainController id="mainController" />
<!-- ===================================================================== -->
<mx:Fade id="fadeInApp" alphaFrom="0" alphaTo="1" duration="500" />
<mx:VBox width="100%" height="100%" backgroundAlpha=".88"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
verticalAlign="top" horizontalAlign="center">
<mx:ViewStack id="vsAppLevel" width="100%" height="100%" paddingTop="0"
creationPolicy="all">
<mx:Canvas id="loginView" showEffect="Fade" hideEffect="Fade">
<Main:Login width="100%" height="100%" />
</mx:Canvas>
<mx:VBox width="100%" height="100%"
id="applicationView" backgroundAlpha="0"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
paddingTop="0"
verticalAlign="top" horizontalAlign="center"
showEffect="Fade" hideEffect="Fade">
<mx:VBox width="985" height="735"
paddingTop="0" verticalGap="0"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
borderStyle="solid" borderThickness="2"
backgroundColor="#FFFFFF"
backgroundAlpha=".75">
<Main:Header backgroundColor="red" />
<mx:VBox width="100%" height="100%">
<Main:MainView />
</mx:VBox>
<Main:Footer backgroundColor="blue"/>
</mx:VBox>
</mx:VBox>
</mx:ViewStack>
</mx:VBox>
</mx:Application>


The footer is trivial and will have no function other than to display company information.
The header will have navigation components that change the view state of the application.

Header.mxml (com.af.clientmanager.view.Main)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="75">
<mx:Script>
<![CDATA[
import com.af.clientmanager.view. ➥
dialogs.DeleteClientConfirmationDialog;
import mx.managers.PopUpManager;
import com.af.clientmanager.control. ➥
commands.events.DeleteClientEvent;
import com.af.clientmanager.control. ➥
commands.events.InsertClientEvent;
import mx.events.ItemClickEvent;
import com.af.clientmanager.model.vo.ClientVO;
import com.adobe.cairngorm. ➥
control.CairngormEventDispatcher;
import com.af.clientmanager.control. ➥
commands.events.UpdateClientEvent;
import mx.controls.Alert;
import com.af.clientmanager.model.ModelLocator;

[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
private function clearClientDetails():void
{
// remove all information from
// the panels after pop-up
model.selectedClient = new ClientVO();
}
private function saveClient():void
{
if(model.selectedClient.objectIdentifier > 0)
CairngormEventDispatcher.getInstance().dispatchEvent(new
UpdateClientEvent(model.selectedClient));
else
{
CairngormEventDispatcher.getInstance().dispatchEvent(new
InsertClientEvent(model.selectedClient));
}
}
private function deleteClient():void
{
// Don't try to delete unless a client is selected
if(model.selectedClient.objectIdentifier > 0)
{
var pop1:DeleteClientConfirmationDialog =
DeleteClientConfirmationDialog(
PopUpManager.createPopUp(this,
DeleteClientConfirmationDialog, true));
pop1.confirmationMessageTitle =
"Delete Confirmation";
pop1.confirmationMessageBody =
"Are you sure you want to delete: " +
model.selectedClient.clientName;
PopUpManager.centerPopUp(pop1);
}
}
private function ➥
functionsBBClick(event:ItemClickEvent):void
{
switch (event.index)
{
case 0:
clearClientDetails();
break;

case 1:
saveClient();
break;
case 2:
deleteClient();
break;
}
}
private function ➥
componentsBBClick(event:ItemClickEvent):void
{
switch (event.index)
{
case 0:
model.MainViewStackState=
model.CLIENT_MANAGER;
break;
case 1:
model.MainViewStackState=
model.PROJECT_ADMIN;
break;
case 2:
model.MainViewStackState=
model.DASHBOARD;
break;
}
}
</mx:Script>
<mx:HBox width="100%" height="100%">
<mx:Image width="190" height="74"/>
<mx:VBox width="100%" height="100%" verticalAlign="bottom">
<mx:HBox width="100%">
<mx:Button label="N" click="clearClientDetails()"/>
<mx:Button label="S" click="saveClient()"/>
<mx:Button label="D" click="deleteClient()"/>
<mx:Spacer width="100%" />
<mx:Button label="Client Manager"
click="{model.MainViewStackState=model.CLIENT_MANAGER}"/>
<mx:Button label="Project Administrator"
click="{model.MainViewStackState=model.PROJECT_ADMIN}"/>
<mx:Button label="Dashboard"
click="{model.MainViewStackState=model.DASHBOARD}"/>
</mx:HBox>
</mx:VBox>
</mx:HBox>
</mx:Canvas>

Footer.mxml (com.af.clientmanager.view.Main)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="25">
<mx:Label text="Footer" fontSize="16" color="white" fontWeight="bold" />
</mx:Canvas>


Main View

The MainView component holds the major aspects of the application and drives which part of the application is visible based on its ViewStack state.

MainView.mxml (com.af.clientmanager.view.Main)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
backgroundColor="#888888"
xmlns:ClientDetailViews=
"com.af.clientmanager.view.ClientManager. ➥
ClientDetailViews.*"
xmlns:ClientFinancialsDetailViews=
"com.af.clientmanager.view.ClientManager. ➥
ClientFinancialsDetailViews.*"
xmlns:ClientProjectsDetailViews=
"com.af.clientmanager.view.ClientManager. ➥
ClientProjectsDetailViews.*"
xmlns:components="com.af.clientmanager.view. ➥
ClientManager.components.*"
xmlns:ClientManager="com.af.clientmanager.view. ➥
ClientManager.*"
xmlns:ProjectManager="com.af.clientmanager.view. ➥
ProjectManager.*"
xmlns:Dashboard="com.af.clientmanager.view.Dashboard.*">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>


<mx:HBox width="100%" height="100%">
<components:ClientList />
<mx:ViewStack id="vsMain" width="100%" height="100%"
paddingTop="0"
creationPolicy="all"
selectedIndex="{model.MainViewStackState}">
<mx:Canvas id="clientManagerView"
showEffect="Fade" hideEffect="Fade">
<ClientManager:ClientManagerView />
</mx:Canvas>
<mx:Canvas id="projectManagerView"
showEffect="Fade" hideEffect="Fade">
<ProjectManager:ProjectManagerView />
</mx:Canvas>
<mx:Canvas id="dashboardView" showEffect="Fade"
hideEffect="Fade">
<Dashboard:DashboardView />
</mx:Canvas>
</mx:ViewStack>
</mx:HBox>
</mx:Canvas>


The main view contains several namespace definitions. Those alias definitions will grant
you access to use components in their locations. This view contains and maintains ClientList, ClientManagerView, ProjectManagerView, and DashboardView.


Client Manager View

Another view of interest is the client manager view.

ClientManagerView.mxml (com.af.clientmanager.view)


<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
xmlns:ClientDetailViews="com.af.clientmanager.view. ➥
ClientManager.ClientDetailViews.*"
xmlns:ClientProjectsDetailViews="com.af.clientmanager.view. ➥
ClientManager.ClientProjectsDetailViews.*"
xmlns:ClientFinancialsDetailViews="com.af.clientmanager. ➥
view.ClientManager.ClientFinancialsDetailViews.*"
xmlns:components="com.af.clientmanager.view. ➥
ClientManager.components.*">

<mx:VBox width="100%" height="100%">
<mx:HBox>
<mx:Button label="Overview"
click="{vsAppLevel.selectedChild=
clientOverview}"/>
<mx:Button label="Projects"
click="{vsAppLevel.selectedChild=
clientProjects}"/>
<mx:Button label="Financials"
click="{vsAppLevel.selectedChild=
clientFinancials}"/>
</mx:HBox>
<mx:ViewStack id="vsAppLevel" width="100%" height="100%"
paddingTop="0"
creationPolicy="all">
<mx:Canvas id="clientOverview"
showEffect="Fade" hideEffect="Fade">
<ClientDetailViews:ClientOverview />
</mx:Canvas>
<mx:Canvas id="clientProjects"
showEffect="Fade" hideEffect="Fade">
<ClientProjectsDetailViews:ClientProjects />
</mx:Canvas>
<mx:Canvas id="clientFinancials"
showEffect="Fade" hideEffect="Fade">
<ClientFinancialsDetailViews:ClientFinancials />
</mx:Canvas>
</mx:ViewStack>
<mx:HBox width="100%" height="40%">
<components:IssuesMilestonesList />
<components:DocumentsList />
</mx:HBox>
</mx:VBox>
</mx:VBox>


Client Overview

The last piece of the application we will inspect here is ClientOverview.mxml. That is about as deep as we can go to fully string together the application.

ClientOverview.mxml (com.af.clientmanager.view.ClientManager.ClientDetailViews)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx=http://www.adobe.com/2006/mxml
xmlns:ClientDetailViews=
"com.af.clientmanager.view.ClientManager.ClientDetailViews.*"
width="100%" height="100%" >
<mx:TabNavigator borderStyle="solid" width="100%" height="100%">
<mx:VBox label="General Information"
width="100%" height="100%">
<ClientDetailViews:clientInfo />
</mx:VBox>
<mx:VBox label="Description" width="100%" height="100%">
<ClientDetailViews:clientDescription />
</mx:VBox>
<mx:VBox label="Location" width="100%" height="100%">
<ClientDetailViews:clientLocation />
</mx:VBox>
<mx:VBox label="Contacts" width="100%" height="100%">
<ClientDetailViews:clientContacts />
</mx:VBox>
<mx:VBox label="Links" width="100%" height="100%">
<ClientDetailViews:clientLinks />
</mx:VBox>
</mx:TabNavigator>
</mx:Canvas>


Flex RemoteObject Configuration


To communicate with the Spring services, you will need to configure a transport protocol in Flex to call those services. The Spring services have been built to return a binary object to Flex. For Flex to call those services, a RemoteObject definition needs to be added to the Cairngorm ServiceLocator. This definition needs to specify a destination that matches the destination running in context in af_Central with BlazeDS.

Services.mxml (com.af.clientmanager.services)


<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cairngorm="com.adobe.cairngorm.business.*">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="clientService"
destination="clientService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="invoiceService"
destination="invoiceService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="mediaService"
destination="mediaService"
showBusyCursor="true">
</mx:RemoteObject>

<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="projectService"
destination="projectService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="secService"
destination="secService"
showBusyCursor="true">
</mx:RemoteObject>
</cairngorm:ServiceLocator>


Notice that each service has an endpoint that points to the af_Central AMF gateway running locally on a Tomcat web server. It is stored as a constant on the model, so we need to change it in only one place when this application is moved to a production environment.


public const SERVICE_ENDPOINT= "http://localhost:8080/af_Central/spring/messagebroker/amf";


The other counterpart to the ServiceLocator is the destination found in the SERVICE_
ENDPOINT. The destination is defined in the applicationContext.xml file, since we are using Spring Blaze Integration (SBI) to expose service beans for Flex Remoting


The destinations in the Services.mxml definitions match the exposed service beans
in applicationContext.xml:


<bean id="clientService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="clientServiceBean"/>
</bean>
<bean id="projectService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="projectServiceBean"/>
</bean>
<bean id="invoiceService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="invoiceServiceBean"/>
</bean>

<bean id="mediaService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="mediaServiceBean"/>
</bean>
<bean id="secService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="secServiceBean"/>
</bean>


we are using SBI to handle communication between Flex and Spring. If we were using SpringFactory instead to expose Spring beans for Remoting, we would set our RemoteObject destinations in the remoting-config.xml


<destination id="secService">
<properties>
<factory>spring</factory>
<source>secService</source>
</properties>
</destination>
<destination id="clientService">
<properties>
<factory>spring</factory>
<source>clientService</source>
</properties>
</destination>


If you compare the destination tag on the RemoteObject to the id on the remoting-config.xml destinations, you will notice they match. This is how the service is located and a method can be called from Flex. The factory definition for Spring wires the destination service in the remotingconfig.xml file to Spring’s WebApplicationContext

Leave a Reply

Subscribe to Posts | Subscribe to Comments

About This Site

Howdy! My name is Suersh Rohan and I am the developer and maintainer of this blog. It mainly consists of my thoughts and opinions on the technologies I learn,use and develop with.

Blog Archive

Powered by Blogger.

- Copyright © My Code Snapshots -Metrominimalist- Powered by Blogger - Designed by Suresh Rohan -