Blog Zenika

#CodeTheWorld

IoT & Mobilité

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:
Example1_Sencha.JPG
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.
 
Capture__home.png
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}
}

datePicker.png
Et voici en image le panel d’ajout d’évènement :
Capture_new_Event_2.png
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.
capture_home_2.png

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é.
Capture_map_2.png
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

11 réflexions sur “Sencha Touch : un framework HTML5 pour Mobile

  • zezeteepouseX

    C’est le meilleur tuto que j’ai vu sur le sujet, voire le seul pour plusieurs domaines, en particulier pour ce qui concerne le localstorage. Merci pour le partage.

    Répondre
  • Très bon tutorial en effet.

    Par contre, j’ai un problème : mon appli gère des clients, et si j’affiche mon panel listant les clients, et qu’ensuite j’affiche le formulaire de création et que je le valide, je me retrouve avec cette erreur :
    « Uncaught TypeError: Cannot call method ‘toLowerCase’ of undefined »
    Alors que si je créé mon client sans avoir été sur la liste avant, cela fonctionne. Visiblement, ça plante au moment du add :
    « 
    var newClient = Ext.ModelMgr.create({},’Client’);
    table_clients.add(newClient); //table_clients correspond à mon objet Store
    « 

    Si quelqu’un a une piste parce que je galère depuis quelques heures.

    Répondre
  • shadjiat

    Bonjour,
    je viens de voir ton commentaire. Je ne sais pas si le problème est toujours d’actualité, mais quand tu dis que tu arrive à créer le client à un moment donné, est ce qu’il s’affiche bien sur la liste? le problème vient, je pense, du panel affichant la liste des clients (et pas de l’ajout), ou alors de la navigation entre le formulaire et la liste. Je n’ai cependant jamais eu cette erreur.
    Sinon, le conseil que je peux te donner pour débugger ton appli, c’est d’utiliser le script « sencha-touch-debug », qui n’est pas compressé et ca te permettra de voir la ligne exacte qui plante.
    Si tu as réussi à résoudre ton problème, ça m’intéresserai de connaitre la solution.

    Répondre
  • Finalement j’ai trouvé. En fait, le problème vient du fait que je génère ma liste au lancement de l’application et non dynamiquement. Du coup, avant de faire mon ‘add’, il faut que je détruise la liste, pour la reconstruire après :

    listeClients.destroy(); //listeClients est un Ext.List
    var newClient = Ext.ModelMgr.create({},’Client’);
    table_clients.add(newClient);
    listeClients = new Ext.List({…})
    _

    J’imagine qu’il y avait un conflit ou quelque chose comme ça.

    Sinon, encore merci pour ce tuto, il m’a beaucoup aidé à « maitrisé » SenchaTouch.

    Répondre
  • Francis

    bravo! C’est un très bon tuto. Il n’en pleut pas des tuto sur le web concernant sencha touch. À ce que je sache, il n’y a qu’un livre sur le sujet en vente fait par Manning, quelqu’un sait pourquoi c’est si peu populaire? Aucun livre sur packtub, aucun tuto sur lynda.com! merci encore.

    Répondre
  • Bonjour,
    je ne comprends pas quels fichiers il faut inclure dans son application.
    Parce que le SDK fait quand même 200Mo. Donc est-ce que je copie que les deux fichiers js et css ou est-ce que je dois copier les dossier ressources et build ?

    Merci.

    Répondre

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

En savoir plus sur Blog Zenika

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Continue reading