1. Gestion de la liste des contacts

1-1. Modification d'un contact

Pour continuer la réalisation du tutoriel nous allons poursuivre à partir du fichier zip téléchargeable ici, celle-ci contient l'ensemble du code source de la partie 1 avec les fichiers nécessaires à la partie 2 que l'on va compléter pendant ce tutoriel.

Pour importer ce projet dans votre workspace, il faudra refaire la même manipulation que dans la partie 1, suivez donc cette procédure.

L'ajout, la modification et la suppression d'un contact utilisent la même procédure que pour la récupération de la liste de contacts, les seules différences sont dans le traitement de la réponse du service. Pour mettre en place ces fonctionnalités, il faut tout d'abord modifier MainView et son helper pour déclencher l'événement de mise à jour d'un contact.

 
Sélectionnez

//view/MainView.mxml
...
<mx:HBox
	x="800"
	styleName="saveBox"
	id="saveBox"
	moveEffect="{Move}"
	width="250"
	height="40">
	<mx:Label text="Save changes ?"/>
	<mx:Button
		icon="{EmbeddedMedia.getInstance().saveContactImg}"
		buttonMode="true" 
		styleName="btnTopContact" 
		width="30" 
		height="30"
		click="{helper.updateContact()}"
		/>
...	

Quand l'utilisateur clique sur le bouton pour faire la mise à jour, la méthode updateContact dans MainViewHelper est appelée pour lancer le traitement par le contrôleur grâce à la méthode updateContact :

 
Sélectionnez

	//helper/MainViewHelper.as
	...
	/**
	 * passe à l'état BTN_SAVE_VIEW_STATE : saveBox apparait / topButtonContact disparait
	 * appel à la methode updateContact dans MainController
	 * */
	public function updateContact(): void
	{
		getMainView().currentState = BTN_SAVE_VIEW_STATE;
		if(getMainView().leftSmartForm.isUpdate)
		{
			switchMode();
			var updatedContactVO : ContactVO = getCurrentContact();
		MainController.getInstance().updateContact( updatedContactVO );
		}
	}
	/**
	 * Créer un contactVO du contact actuellement sélectionné 
	 * @return ContactVO
	 * */
 
	public function getCurrentContact() : ContactVO
	{
		var dtLeft : DictionaryTable = getMainView().leftSmartForm.source;
		var dtRight : DictionaryTable = getMainView().rightSmartForm.source;
		var dtBottom : DictionaryTable = getMainView().bottomSmartForm.source;
		dtLeft.putAll( dtRight );
		dtLeft.putAll( dtBottom );
		var contactOut : ContactVO = ContactVO( MapUtils.getObjectFromDictionaryTable( dtLeft, ContactVO ) );
		return contactOut;
	}
...

Le contrôleur appelle le service distant en utilisant la classe ContactUpdateResponder :

 
Sélectionnez

//control/MainController.as
...
/**
 * Appelle updateContact dans businessDelegate et passe en paramètre le responder qui traite la réponse
 * */
public function updateContact( pContactVO : ContactVO ) :void
{
	var updatedContactTO : ContactTO = wrapContactVO( pContactVO );
	getMainBusinessDelegate().updateContact( new ContactUpdateResponder(), updatedContactTO );
}
...

Créer la classe ContactUpdateResponder qui traite la réponse du service distant et appelle le modèle pour le mettre à jour:

 
Sélectionnez

//business/responder/UpdateContactResponder.as
...
public class ContactUpdateResponder implements IBusinessResponder
{
	public function ContactUpdateResponder()
	{
	}
	/**
	 * traitement du résultat renvoyé par le service
	 * Mise à jour du modèle
	 * */		
	public function result(data:Object):void
	{
		var contactVO : ContactVO = ContactTO( ResultEvent( data ).result ).contacts.getItemAt( 0 ) as ContactVO;  
MainController.getInstance().getContactModel().updateThisContact( contactVO );
	}
	public function fault(info:Object):void
	{
		Alert.show("Error updating the contact !");
	}	
}

Le modèle met à jour la liste de contacts et notifie les « observers » qui vont rafraîchir l'affichage :

 
Sélectionnez

//model/ContactModel.as
...
**
 * Mise à jour du contact ( en fonction du contactId )
 * Déclenche l'événement indiquant que la liste de contact à changé
 * */
public function updateThisContact( pContactVO : ContactVO ) : void
{
	var idx : int = 0;
	for each ( var contact : ContactVO in _contacts){
		if( contact.contactId === pContactVO.contactId )
		{
			_contacts.setItemAt( pContactVO, idx );
			_contacts.refresh();
			notifyObservers( new ContactListNotification( null ) );
			break;
		}
		idx++;
	}
}
...

1-2. Ajout d'un contact

L'ajout et la suppression, utilisent exactement le même processus sauf que le traitement est différent dans le modèle. Dans le cas de l'ajout d'un contact :

 
Sélectionnez

//model/ContactModel.as
...
**
 * crée un nouveau contact dans _contacts
 * Déclenche l'événement indiquant que la liste de contact à changé
 * */
public function createContact( pContactVO : ContactVO ) :void
{
	_contacts.addItem( pContactVO );
	_contacts.refresh();
	notifyObservers( new ContactListNotification( null ) );
}
...

1-3. Suppression d'un contact

Lors de la suppression d'un contact, on demande une validation grâce à la classe Alert :

 
Sélectionnez

//helper/MainViewHelper.as
/**
		 * Alert demandant confirmation avant suppression du contact
		 * */
 
		public function confirmDeleteContact( e : Event ) : void
		{
			Alert.show("Do you really want to remove "+ selectedContact.firstName.toString() + " " + selectedContact.lastName.toString() + " from your contact list ?",
			"Remove " + selectedContact.firstName.toString() + " " + selectedContact.lastName.toString() + " :",
			3, null, deleteContact);
		}

Dans le cas de la suppression d'un contact :

 
Sélectionnez

//model/ContactModel.as
...
/**
 * supprime un contact ( en fonction du contactId )
 * Déclenche l'événement indiquant que la liste de contact à changé
 * */
public function deleteThisContact( pContactVO : ContactVO ) :void
{
	var idx : int = _contacts.getItemIndex( pContactVO );
	var i : int = 0;
	for each (var contact : ContactVO in _contacts)
	{
		if (contact.contactId == pContactVO.contactId)
		{
			_contacts.removeItemAt( i );
			_contacts.refresh();
			notifyObservers( new ContactListNotification( null ) );
		}
		i++;
	} 
}
...

2. Création du moteur de recherche

Le fonctionnement du « Search Tree » est composé de deux étapes distinctes : premièrement, il faut indexer les données. « Search Tree » crée un graphe d'expressions de recherche et relie les mots aux objets qui les contiennent, d'après les options émises sur les éléments d'objet; deuxièmement, l'arborescence de recherche est à même d'effectuer des recherches en parcourant le graphe d'après une chaîne de caractères qui lui a été soumise. Cette technique est beaucoup plus performante qu'une recherche directe sur la collection, car le plus gros du travail est effectué durant la phase d'indexation. Plus la chaîne de caractères soumise est longue, plus le nombre de possibilités sur le graphe est mince.

2-1. Première étape : construire l'indexation

L'indexation est créée à l'aide des classes SearchTree et SearchProperty. Pour chaque propriété des objets de valeur à indexer, une SearchProperty doit être créée et un type d'indexation doit être défini. Les propriétés sont ensuite utilisées pour créer l'indexation.

 
Sélectionnez

//helper/MainViewHelper.as
...
/**
 * passe à l'état BASE_VIEW_STATE
 * appelle à la fonction initSearchTree
 * déclare le premier contact de la liste comme le contact sélectionné
 * */
public function refreshView() : void
{
	baseContactView();
	initSearchTree();
	getMainView().contactList.selectedItem = getContactModel().getContacts()[0] as ContactVO;
	dispatchEvent(new Event('selectedContactChange') );
}
...
/**
 * on ContactListNotification :
 * actualise la vue
 * */
override public function update( o : IObservable, n:Notification) :void
{
	dispatchEvent( new Event("contactListChange") );
	refreshView();
}
...
/**
 * déclaration du tableau searchProperties : firstName, lastName
 * début de l'indexation
 * */	
public function initSearchTree() : void
{
	_tree = new SearchTree();
	var searchProperties : Array = 
	[
	new SearchProperty( "firstName", SearchProperty.INDEX_TYPE_CONTAINS ),
	new SearchProperty( "lastName", SearchProperty.INDEX_TYPE_CONTAINS )
	];
_tree.index( getContactModel().getContacts().source , searchProperties );
}
...

Le type d'indexation impactera sur les performances plus que le nombre d'objets à indexer ou à rechercher. Le graphe de mots résultant d'une indexation à l'aide de INDEX_TYPE_CONTAINS sera plus complexes.

2-2. Seconde étape : effectuer la recherche

Pour effectuer la recherche, nous utilisons simplement la méthode de recherche avec une String comme critère de recherche.

 
Sélectionnez

//helper/MainViewHelper.as
...
/**
 * on application init : renvoie par défaut tous les contacts sélectionnés dans le modèle
 * Lors d'une recherche : renvoie le résultat de searchTree dans la liste de contacts du modèle
 * */
[Bindable("contactListChange")]
public function get getContactList() : Array
{
	if( getMainView().filterTextInput.text == null || getMainView().filterTextInput.text.length <1 )
	{
		return getContactModel().getContacts().source;
	}else{
		var searchResult : Array = _tree.search( getMainView().filterTextInput.text ).values();
		return searchResult;
	}
}
...

On déclenche l'événement getContactList lors de la saisie dans DelayTextInput :

 
Sélectionnez

//view/MainView.mxml
...
<elements:DelayTextInput
			width="100%"
			id="filterTextInput"
			change="helper.dispatchEvent( new Event('contactListChange') );"
			delay="200" />
...

En remplissant le champ de recherche en haut à gauche de l'application, la liste de contacts est filtrée.

Vous pouvez télécharger le code source de l'application finale ici.