Developping a contact manager application (CManager) – Part 1

Connecting the view to the service

To load our contacts, the application will trigger the following operations sequence :

  • At application startup, the ViewHelper is called to retrieve the contacts’ list.
  • It triggers the getContactList method on the controller.
  • The controller creates a Responder instance (as explained below).
  • The controller asks the BusinessDelegate to launch a call to the remote service.
  • The BusinessDelegate calls the service and retrieve an ACT (Asynchronous Completion Token).
  • The BusinessDelegate sends the ACT to the Responder instance.
  • The service answers back and the ACT-linked Responder is triggered.
  • The Responder handles service returns and updates the value in the model.
  • A notification is sent to the observers
  • The ViewHelper can then retrieve data from the model and render the view accordingly.
Remote Services - Lifecycle

Remote Services - Lifecycle

Handling service returns (responder)

The main purpose of a responder is to manage the responses of a specific remote service method. Every responder should implement the IBusinessResponder interface. The fault and result methods are intended for handling the system errors (network failure for example) or to propagate the results.
In our present case, the ContactListResponder’s result method triggers the setter method into the ContactModel.

//business/responder/ContactListResponder.as
...
public class ContactListResponder implements IBusinessResponder
{
	public function ContactListResponder()
	{
	}
	public function result(data:Object):void
	{
		//we will handle the service response in this function
	}
	public function fault(info:Object):void
	{
		Alert.show("Error getting contact list !");
	}
}
...

Please verify your imports.

Rendering data

We start by creating a method into the MainViewHelper to trigger the retrieveContactList method into the controller.

//helper/MainViewHelper.as
...
		/**
	 *  get the contact list
	 * */
	public function retrieveContactList() : void
	{
		MainController.getInstance().getContactList();
	}
...

Please verify your imports.

We update MainView and its MainViewHelper. Then we just have to add the dataProvider property to the List component, plus one Button to retrieve the contacts’ list.

//view/MainView.mxml
//view/MainView.mxml
...
<HBoxView
xmlns="org.servebox.foundry.view.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:comp="org.servebox.sample.contactapplication.component.*"
width="{MainConf.SCENE_WIDTH}" height="{MainConf.SCENE_HEIGHT}"
xmlns:form="org.servebox.toolbox.form.*"
xmlns:elements="org.servebox.toolbox.form.elements.*"
xmlns:helper="org.servebox.sample.contactapplication.helper.*"
horizontalAlign="center"
horizontalCenter="0"
>
...
<mx:Button
id="getBtn"
icon="{EmbeddedMedia.getInstance().getContactImg}"
click="{helper.retrieveContactList()}"
/>
...
<mx:List
width="300"
height="648"
id="contactList"
dataProvider="{helper.getContactList}" />
...

Please verify your imports.

We create a binding on the getter method in the MainViewHelper, to feed the list. If you’re not familiar with binding, you should read this document : http://www.adobe.com/devnet/flex/quickstart/using_data_binding/.

//helper/MainViewHelper.as
...
	/**
	 * return the contact array from the model
	 * */
	[Bindable("contactListChange")]
	public function get getContactList() : Array
	{
		return getContactModel().getContacts().source;
	}
	/**
	 * Shortcut to ContactModel
	 * */
	public function getContactModel() : ContactModel
	{
	return ContactModel(
			ModelLocator.getInstance().getModel(
				getQualifiedClassName( ContactModel )
			)
		);
	}

Please verify your imports.

Creating the BusinessDelegate

Before adding the method to the controller, we must create our « BusinessDelegate ». When you use the AS Foundry framework, you should create a BusinessDelegate instance, in order to allow interactions between the remote service and the MVC participants. One of the main benefits is that changes made on the remote service won’t interfere with any other application’s components except the BusinessDelegate itself. There are other benefits to use the BusinessDelegates that you can figure out in the « Remote procedure call » article.

We create the MainBusinessDelegate into the package com.servebox.sample.contactapplication.business. Then we create the getContactList method wherein we link the ACT (Asynchronous Completion Token) to a IBusinessResponder instance. The responder handles the service response.

//business/MainBusinessDelegate.as
...
public class MainBusinessDelegate extends AbstractBusinessDelegate implements IBusinessDelegate
{
	public function MainBusinessDelegate( service : Object=null )
	{
		super(service);
	}
	/**
	 * call the java request getContactList()
	 * */
	public function getContactList( responder : ContactListResponder ) : void
	{
		linkResponderToCallToken( getService().getContactList() , responder );
	}
...

Please verify your imports.

We initialize the « BusinessDelegate » by overriding the initializeDelegates method into the controller. The remote service call is performed by the controller using the ContactListResponder class as « responder » (that we are going to create, later on).

//control/MainController.as
...
/**
* get java service
 * */
private function get service() : RemoteObject
{
	var service : RemoteObject = new RemoteObject("java-service");
	service.channelSet = ChannelSetProvider.getInstance().getDefaultChannelSet();
	return service;
}
/**
 * initialize business delegate
 * */
override public function initializeDelegates():void
{
	var bd : MainBusinessDelegate = new MainBusinessDelegate( service );
	registerBusinessDelegate( bd , getQualifiedClassName( MainBusinessDelegate ) );
}
/**
* call the businessDelegate function to get contact list
 * */
public function getContactList() : void
{
	getMainBusinessDelegate().getContactList( new ContactListResponder() );
}
...

Please verify your imports.

Handling the service’s response

The getContactList method sends us back a ContactTO-typed TransferObject.

//service/value/transfer/ContactTO.as
package org.servebox.sample.contactapplication.service.value.transfer
{
	import mx.collections.ArrayCollection;
	import org.servebox.foundry.transfer.TransferObject;
	[Bindable]
[RemoteClass(alias="org.servebox.sample.contactapplication.service.value.transfer.ContactTO")]
	public class ContactTO extends TransferObject {
 
		private var _contacts : ArrayCollection;
 
		public function set contacts( value : ArrayCollection ) : void
		{
			_contacts= value;
		}
 
		public function get contacts() : ArrayCollection
		{
			 return _contacts;
		}
	}
}
...

Please verify your imports.

The ContactVO class is a representation of our contact :

//service/value/vo/ContactVO.as
package org.servebox.sample.contactapplication.service.value.vo
{
	import org.servebox.foundry.value.AbstractValueObject;
	[Bindable]
[RemoteClass(alias="org.servebox.sample.contactapplication.service.value.vo.ContactVO")]
	public class ContactVO extends AbstractValueObject implements IContactVO {
 
		private var _firstName : String;
		private var _lastName : String;
...
 
Last step, we must handle the service response in the ContactListResponder :
//business/responder/ContactListResponder.as
...
public function result(data:Object):void
{
	var contactTO : ContactTO = ResultEvent( data ).result as ContactTO;
MainController.getInstance().getContactModel().setContacts( contactTO.contacts );
}
...

Please verify your imports.

As we save our contact into the model, a notification is sent to all the « observers » and all the views are updated. When you click on the button to retrieve the contacts, you can now see your contacts’ list.

Displaying contact’s information

When we select a contact from the « list », we must display the whole contact’s data into the form. We create a « binding » that send us back the selected contact from the « list » :

//helper/MainViewHelper.as
...
/**
* When the event "selectedContactChange" is dispatched
* get the list selected item as a ContactVO
 * */
[Bindable("selectedContactChange")]
public function get selectedContact():ContactVO
{
	return ContactVO( getMainView().contactList.selectedItem );
}
...

Please verify your imports.

The selectedContactChange event is triggered when the « list »’s selected item changes :

//view/MainView.as
...
<mx:List
styleName="listBox"
width="300"
height="648"
id="contactList"
rowHeight="{Math.floor( contactList.height / 9 )}"     dataProvider="{helper.getContactList}"
itemRenderer="{new ClassFactory( ContactRenderer )}"
change="helper.dispatchEvent(new Event('selectedContactChange'))">
...

Please verify your imports.

In the following step, we update MainView by feeding the « SmartForm » components’ source property and triggering the selectedContact getter method.

//view/MainView.as
...
<form:SmartForm
id="leftSmartForm"
styleName="form"
width="50%"
source="{helper.selectedContact}">
...
<form:SmartForm
id="rightSmartForm"
styleName="form"
width="50%"
source="{helper.selectedContact}">
...
<form:SmartForm
id="bottomSmartForm"
styleName="form"
width="50%"
source="{helper.selectedContact}">
...

When we use the SmartForm, the ID property of each form item is linked to the properties of the object retrieved with the selectedContact method. The AS Foundry SmartForm makes it easier to insert and update data through the forms.
SmartForm uses object copies instead of references, which allows for example to enable the user to cancel its modification.
SmartForm allows for easy switch between READ mode and UPDATE mode.

You will find more information about SmartForm in the article Using SmartForm.

Download the CManager source code (first part tutorial).

In the following part of this tutorial, we will see how to create / update / delete contacts and set up a search engine.

Page 1 Page 2 Page 3

Friday, April 24th, 2009 No Comments by Wajdi Hadj ameur