Site icon Blog Zenika

Sencha Touch : un framework HTML5 pour Mobile

Sencha Touch est un framework JavaScript spécialement conçu pour développer des applications web mobiles. Ce produit est développé par l’entreprise Sencha, anciennement nommée ExtJS, et qui s’est étendue avec jQTouch et Raphael.

La particularité de ce framework est son développement presque exclusif en JavaScript. Sencha touch est compatible avec les platformes android, iOS (iphone, ipod touch, ipad) ainsi que le tout dernier BlackBerry 6. Il est en particulier adapté à toutes les résolutions d’écran. Il ne s’exécute cependant que sur les navigateurs webkit. Le framework est disponible en version 1.1 (sortie le 24 mars), sous une licence open source GPL3 et une licence commerciale gratuite.
Dans cet article, vous allez apprendre comment utiliser Sencha Touch pour construire des « WebApps » riches et performantes pour votre mobile. Je reprendrai mon application Agenda précédemment développée avec jQTouch et jQuery Mobile.

Approche

Le développement avec Sencha diffère singulièrement des autres frameworks que j’ai présentés. En effet, quand jQTouch et jQueryMobile se contentent de fournir du CSS et d’améliorer un site web pour l’adapter au mobile, Sencha Touch fourni des composants d’UI en JavaScript et en génère ensuite les éléments HTML.
Alors comment utiliser ce framework? Commençons par créer une page HTML. Dans cette page, au niveau de la balise head, doivent être référencés deux fichiers du framework : « sencha-touch.js » et « sencha-touch.css ». Trois autres fichiers CSS sont fournis avec le framework: « apple », « android » et « bb6 » (pour Blackberry) qu’on peut utiliser pour remplacer le thème « sencha-touch ». Par ailleurs, la derniere version du framework fourni un script « autotheme.js » qui permet de changer automatiquement de thème suivant la platforme.
Le reste du document, body, reste vide. A présent, passons à la création des pages de l’application avec les composants Sencha.
On commence par initialiser l’application :

Ext.setup({
    tabletStartupScreen: 'tablet_startup.png',
    phoneStartupScreen: 'phone_startup.png',
    icon: 'icon.png',
    glossOnIcon: false,
    onReady: function() {
            //création du panel et des composants
    }
});

La fonction Ext.setup organise l’application. Un certain nombre de paramètres peuvent être spécifiés comme l’écran de démarrage ou l’icone, et dans le paramètre onReady, on spécifie la fonction qui sera exécutée quand le DOM est prêt. C’est au sein de cette fonction qu’on défini les éléments qui seront affichés par le framework.
Voici comment on défini un composant Sencha Touch :

var objectName = new Ext.ComponentName({
     /*objectDefinition*/
});

Parmis ces composants, on peut créer un panel avec Ext.Panel :

var panel = new Ext.Panel({
    fullscreen: true,
    dockedItems: [{
            dock : 'top',
            xtype: 'toolbar',
            title: 'Home'
        }],
    items: [{
            html: '<h1>Welcome</h1>'
    }]
});

L’objet docketItems permet d’organiser des éléments en « dock » et est utilisé pour afficher les barres d’outils. Ce qu’il faut savoir, c’est que Sencha Touch permet d’ajouter des composants de deux manières différentes : soit « anonymement » avec le paramètre xtype (ici avec ‘toolbar’), soit en créant l’objet du composant correspondant. Ainsi pour cet exemple, on aurait pu créer à part un composant de type Ext.Toolbar et l’ajouter ensuite à dockedItems pour l’afficher dans le panel. Ensuite dans items, on spécifie le contenu du panel. Ici, j’ai écrit simplement du code HTML. Et voici ce que l’on obtient:

Plus généralement, le paramètre items peut contenir n’importe quels composants du framework. Pour les organiser, un paramètre doit être configuré : layout. Il existe plusieurs types de layout : ‘auto’, ‘card’, ‘fit’, ‘hbox’, ‘vbox’. D’autre part, on peut également organiser des panels en onglets avec Ext.TabPanel, ou en carousel avec Ext.Carousel. Petite précision : les composants pouvant contenir d’autres composants, comme Panel ou TabPanel, héritent d’un conteneur plus général : Ext.Container.

Démonstration

Pour la démonstration, j’ai repris mon application Agenda développée avec jQTouch et jQuery Mobile, pour la réaliser avec Sencha Touch. Cette fois, le développement étant très différent des deux autres frameworks, l’application a du être entièrement réécrite. Je rappelle une dernière fois en quoi elle consiste : une page permet de créer un évènement et de le sauvegarder sur le LocalStorage du navigateur, la page d’accueil liste les évènements et pour chacun on peut accéder à une page donnant les informations entrées. Parmi ces informations, se trouve l’emplacement de l’évènement, qu’on peut afficher sur une Google Map dans la page suivante.

Les panels

L’application se compose donc de 4 panels. J’ai défini chacun de ces panels dans un fichier JavaScript, et j’ai également un fichier JavaScript central dans lequel j’organise ces panels. Je commence par créer le panel d’accueil :

homePanel = new Ext.Panel({
	    fullscreen: true,
	    dockedItems: [{
	    	 	xtype: 'toolbar',
	    	 	items: [{xtype: 'spacer'}, newEventButton],
	    	 	title: 'Agenda',
	    	 	dock: 'top'
	    	}]
});

Ce panel se compose donc d’une « toolbar » avec un bouton « newEventButton » :

var newEventButton = new Ext.Button(
    	        {
    	            ui: 'normal' ,
    	            text: 'New',
    	            iconMask: true,
    	            iconCls: 'compose'
    	        }
);

Pour créer un bouton avec Ext.Button, il faut spécifier plusieurs paramètres : ui qui défini la forme, taille et couleur du bouton (‘normal’, ‘back’, ’round’, ‘action’, ‘forward’), et text pour écrire un texte sur le bouton. Par ailleurs, Sencha Touch fourni des icones pour personnaliser les boutons. Pour cela, il suffit de spécifier les paramètres suivants : iconCls pour choisir le type d’icone, et iconMask pour afficher l’icone sur le bouton (mettre à « true »). La liste des différentes icones peut être trouvée dans les dossiers « icons » ou « toolbars » parmi les exemples fournis par le framework.
 

La Liste des évènements n’est pas encore comprise dans le panel, je détaillerai cette partie quand je parlerai du LocalStorage.
Je passe à présent au panel permettant d’ajouter un évènement. Il contient un formulaire pour entrer les différentes informations. Pour créer un formulaire avec Sencha Touch, on utilise la classe Ext.form.FormPanel :

var myform = new Ext.form.FormPanel({
	cls : 'form',
	scroll: 'vertical',
	items: [
             {
               xtype: "textfield",
               name: "name",
               label: "Name",
               placeHolder: "Enter the event's name"
             },
             //...
	]
});

Le paramètre cls permet d’indiquer une classe CSS additionnelle, qui est décrite dans la page HTML. Ici j’ai ajouté cette classe pour spécifier la taille du formulaire et l’afficher sur toute la page. Ensuite, les champs de saisie sont ajoutés à items. Pour chaque champ, il faut spécifier un certain nombre de paramètres, les plus communs étant le type xtype, le nom name, et le label. Par ailleurs, Sencha Touch fourni des classes pour chaque champ avec une interface adapté au mobile. Ainsi, pour un type date, on peut utiliser la classe Ext.DatePicker qui affiche un calendrier sur l’écran.

{
	xtype: "datepickerfield",
	name: "startDate",
	label: "Start Date",
	picker: { yearFrom: 2011, yearTo: 2020}
}


Et voici en image le panel d’ajout d’évènement :

En ce qui concerne le panel qui localise l’évènement, j’utilise l’API JavaScript Google Maps (qu’il faut déclarer dans la page HTML). Pour créer une carte, Sencha Touch fourni une classe qui insère automatiquement une carte Google dans un objet de type Ext.Component. Cette classe est Ext.Map et reprend les paramètres d’une Google Map.

var map = new Ext.Map({
    	mapOptions: {zoom: 15}
});

Ensuite on crée un panel qui va contenir cette carte :

var mapPanel = new Ext.Panel({
        fullscreen: true,
        //docked items...
        items : [map]
});

La classe Ext.Map permet de dimentionner automatiquement la carte pour qu’elle occupe tout le contenu du panel. Pour accéder et modifier la carte, il suffit de récupérer l’objet map de Ext.Map instancié (ici map.map) et ensuite d’utiliser les fonctions fournies dans l’API Google Maps.
Pour finir, dans le script central, j’initialise l’application avec Ext.setup, et je créer un panel de type Ext.Container qui va contenir tous les panels de l’application et les afficher à tour de rôle avec un layout de type card. Pour choisir le panel à afficher on utilise la fonction setActiveItem(i).

Ext.setup({
	phoneStartupScreen : 'phone_startup.png',
	tabletStartupScreen : 'tablet_startup.pgn',
	icon : 'icon_agenda.jpg',
	glossOnIcon : true,
	onReady : function() {
                  //...
                  p = new Ext.Container({
	                fullscreen: true,
	                layout: 'card',
	                cardSwitchAnimation: {
		              type: 'fade',
                              duration: 600
                        },
                        items: [homePanel, newEventPanel, evtPanel, mapPanel]
                  });
});
p.setActiveItem(0);

Pour ce qui est du panel evtPanel qui affiche le détail de chaque évènement, il est crée avec un item vide qui est ensuite enlevé et remplacé par les informations de chaque évènement sélectionné. Je donnerai plus de précision dans la partie suivante.

LocalStorage

Pour gérer les données d’une application, Sencha Touch utilise principalement deux classes : Ext.data.Model et Ext.data.Store.
La classe Ext.data.Model permet de représenter un objet de l’application. Un modèle est défini par une série de champs ainsi que des propriétés et méthodes spécifiques. Parmi eux, la possibilité de définir des contraintes de validation, des associations avec d’autres modèles, ainsi que l’utilisation d’un Proxy.
La classe Ext.data.Proxy permet de gérer la sauvegarde et le chargement des données. Il existe deux sous classes de Proxy : ClientProxy pour gérer les données sur le client, et ServerProxy pour celles qui sont sur le serveur.
L’utilisation de Proxy se fait par l’intermédiaire de la classe Store. Cette classe permet de contenir des données d’un modèle récupérés via un Proxy.
A présent, pour notre application, nous allons utiliser ces classes pour sauvegarder les évènements entrés avec le formulaire sur le LocalStorage du navigateur.
On commence par créer le modèle ‘Event’ avec les mêmes champs que le formulaire et un proxy de type localstorage :

Ext.regModel('Event', {
	fields : [ {
		name : 'name',
		type : 'string'
	}, {
		name : 'startDate',
		type : 'date'
	}, {
		name : 'endDate',
		type : 'date'
	}, {
		name : 'location',
		type : 'string'
	}, {
		name : 'repetition',
		type : 'string'
	}, {
		name : 'alert',
		type : 'boolean'
	}, {
		name : 'email',
		type : 'string'
	} ],
	proxy : {
		type : 'localstorage',
		id : 'EventStore'
	}
});

L’id EventStore va permettre de regrouper les données entrés avec ce modèle sous le même identifiant dans le LocalStorage du navigateur. Ensuite il faut créer un objet Store pour ce modèle :

var store = new Ext.data.Store({
	model : 'Event'
});

Puis, nous allons utiliser ces objets pour ajouter un évènement. On reprend le bouton « save » du formulaire :

saveButton.on('tap', function() {
	var event = Ext.ModelMgr.create({}, 'Event');
	store.add(event);
	myform.updateRecord(event, true);
        store.sync();
	//...
});

On commence par créer une instance du modèle event avec des champs vides et on l’ajoute au store. Ensuite on appelle une méthode updateRecord sur le formulaire qui va remplir l’objet event avec les données entrées. Enfin, on synchronise le store avec le LocalStorage ce qui aura pour effet de mettre à jour les données du store.
Pour récupérer les données, il suffit alors d’appeler la fonction load sur le store :

store.load(function(records, operation, success) {
	for ( var i = 0; i < records.length; i++) {
		console.log(records[i].data.name);
	}
	displayList(store);
});

displayList étant une fonction implémentée dans le script du panel « home » qui va ajouter une liste au panel avec les noms de tous les évènements enregistrés :

function displayList(storeName){
	list = new Ext.List({
		fullscreen:true,
		itemTpl : '{name}',
		indexBar: true,
		store: storeName ,
                listeners: {
	    	itemtap : function(list, index, item, e){
	    		evt = storeName.getAt(index);
	    		displayEventPanel(evt);
	    	}
	    }
	});
	homePanel.add(list);
}

Cette liste à un paramètre store qui référence au nom du store contenant les évènements. Ce paramètre va permettre de mettre à jour la liste automatiquement à chaque changement dans les données sauvegardées. Ensuite à chaque selection d’un item de la liste, on va afficher le panel contenant les informations enregistrées de l’évènement.

Géolocalisation

En ce qui concerne la Géolocalisation de l’utilisateur, Sencha Touch fourni une classe Ext.util.GeoLocation qui est basée sur l’API Geolocation d’HTML5.
 
 
 
 
 

var geo = new Ext.util.GeoLocation({
	autoUpdate : false,
	listeners : {
		locationupdate : function(geo) {
			myPosition = new google.maps.LatLng(geo.latitude, geo.longitude);
			map.update(myPosition);
			var marker = new google.maps.Marker({
				position : myPosition,
				map : map.map,
				title : "Me!"
			});
			findDirection(myPosition); /* affiche un chemin de l'utilisateur vers l'évènement */
		},
		locationerror : function(geo, bTimeout, bPermissionDenied, bLocationUnavailable, message) { }
	}
});

Par défaut, dès qu’elle est instanciée, la classe va constamment suivre la position de l’utilisateur. Le paramètre autoUpdate mis à false permet d’éviter cette mise à jour automatique et la demande de localisation s’effectue en appelant la fonction updateLocation. On récupère ensuite les évènements locationupdate et locationerror. locationupdate contiendra les données de localisation de l’utilisateur si la géolocalisation aboutit. Si elle échoue, l’évènement locationerror est déclanché.

Vous trouverez le code complet de l’application sur le Github de Zenika

Tests et critique

Sencha Touch est un framework très différent des deux autres que j’ai présentés à savoir jQueryMobile et jQTouch. Sencha Touch est une véritable librairie JavaScript organisée en classes. Elle est très riche, avec notamment la prise en charges des API HTML5 comme le localStorage, la géolocalisation, et le support multimédia (audio et vidéo). Le développement avec Sencha Touch, presque entièrement en JavaScript, est un peu déroutant au début mais l’application gagne beaucoup en organisation et en richesse. Coté performance, mis à part un temps de chargement assez long, l’application reste très fluide.

Ressources

Quitter la version mobile