Comme je l’avais dit en introduction, une application Windows 8 style Metro diffère de votre application web classique. Vous devrez notamment jouer avec les APIs offertes par le framework WinJS qui permet de communiquer entre le système est votre application.
Une application « vit » de son lancement à sa fermeture. Tout ce qui se passe entre représente le cycle de vie de l’application. Notez que comme sur les systèmes mobiles, les applications Metro peuvent être fermées à tout moment si le système à besoin de ressources. Il est donc important de bien gérer la vie et la mort de l’application.
Code commun à tous les templates applicatifs
Dans l’article précédent, on a vu les différents templates applicatifs proposés par Visual Studio:
Windows Metro – Les templates pour application JavaScript (Grid, Navigation, Split, …)
Tous ces templates contiennent une page (écran) par défaut, nommée « default.html »:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>BlankApp</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
<script src="//Microsoft.WinJS.0.6/js/base.js"></script>
<script src="//Microsoft.WinJS.0.6/js/ui.js"></script>
<!-- BlankApp references -->
<link href="/css/default.css" rel="stylesheet">
<script src="/js/default.js"></script>
</head>
<body>
<p>Content goes here</p>
</body>
</html>
Celle-ci référence plusieurs fichiers JavaScript « default.js », notamment ceux du framework WinJS. Elle référence aussi un fichier JavaScript qui va contenir le comportement dynamique de la page. De base, voici ce que contient ce fichier:
(function () {
"use strict";
var app = WinJS.Application;
app.onactivated = function (eventObject) {
if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
}
WinJS.UI.processAll();
}
};
app.oncheckpoint = function (eventObject) {
// TODO: This application is about to be suspended. Save any state
// that needs to persist across suspensions here. You might use the
// WinJS.Application.sessionState object, which is automatically
// saved and restored across suspension. If you need to complete an
// asynchronous operation before your application is suspended, call
// eventObject.setPromise().
};
app.start();
})();
On a donc une fonction auto-exécutée, pour permettre de « fermer » le scope du bloc de code à l’intérieur. Grâce à cette fonction, les méthodes déclarées à l’intérieur ne sont pas visibles de l’extérieur, une sorte de « private » en JavaScript.
Petite remarque personnelle, je ne comprend pas vraiment pourquoi Microsoft isole son code comme cela dans tous les exemples. L’utilisation de leur namespace Windows / WinJS suffit à compartimenter le code pour éviter les collisions. Cela rend le code difficile à manipuler par la suite. Bref, j’aurai sûrement pas fait comme cela mais ce n’est pas grave.
La fonction WinJS.Application.start
La méthode WinJS.Application.start est appelée dans tous les templates. C’est elle qui va démarrer un processus important dans le cycle de vie (livecycle) de l’application, celui des évènements qui transitent par l’objet WinJS.Application. Parmi ces évènements, on a les évènements provenant du système comme les évènements de lancement nommés « checkpoint » dans le jargon Win Metro.
La fonction WinJS.Application.oncheckpoint
Dans le code de base, on définit la fonction WinJS.Application.oncheckpoint. Celle-ci sera appelée par le système quand l’application juste avant sa mise en arrière-plan, pendant la bascule vers une autre application par exemple.
C’est à ce moment que vous devrez sauvegarder l’état de l’application (navigation / choix utilisateur). En effet, une fois en arrière plan, votre application peut être « tuée » par le système à n’importe quel moment. Si l’utilisateur relance votre application, vous voudrez le renvoyer là où il était auparavant, vous devrez donc sauvegarder les informations importantes pour les recharger plus tard.
Si vous êtes développeur Flex /AIR, c’est l’équivalent d’un évènement Event.DEACTIVATE propagé sur l’application principale.
La fonction WinJS.Application.onactivated
La fonction onactivate est invoquée par le système au lancement de l’application. A celle-ci, l’application passe un objet qui contient des détails sur le contexte de ce lancement. Vous pourrez notamment savoir si c’est le premier lancement de l’application ou si l’utilisateur est simplement repassé sur votre application alors qu’elle était en arrière-plan.
Pour les devs AIR, c’est l’équivalent de l’évènement flash.events.InvokeEvent.
La fonction WinJS.UI.processAll
A la fin de l’appel à onactivated, vous pouvez remarquer que l’on fait appel à WinJS.UI.processAll. Cette méthode est une des pierres angulaires du framework WinJS. En effet, c’est elle qui indique au framework que tous les composants graphiques doivent être rendus / interprétés.
Comme vous le verrez pas la suite, tout l’interface graphique se fait de manière déclarative par des attributs « data-win-* » sur des éléments HTML. Ces éléments non-standards doivent être interprétés par WinJS qui va les transformer en composants « natifs ».
Quelques mots sur le fonctionnement de WinJS.UI.processAll
A l’appel de processAll, WinJS va donc parcourir tout le DOM à le recherche de ces attributs « data-win-* ». Le processus se fait en fait en 2 passes:
- WinJS recherche tous les tags ayant un attribut « data-win-control ». On le verra plus tard, dans ces attributs, on définit un « décorateur » au sens Design Pattern du terme. C’est-à-dire que l’on définit une classe qui va venir gérer notre composant, son comportement interne et ses interactions. A chaque attribut « data-win-control », WinJS va instancier ce décorateur et l’associer au composant.
- WinJS recherche tous les tags ayant un attribut « data-win-options ». Celui-ci contient les options passées au décorateur instancié en 1/. WinJS va ainsi évaluer toutes les propriétés dynamiques et les setter sur l’instance du décorateur.
Une fois instancié, le décorateur créé à l’étape 1 est accessible par la propriété « winControl » de l’élément HTML.
Mon avis personnel sur ce fonctionnement
Ce fonctionnement est assez atypique et on ne le retrouve pas ce système de décorateur dans le HTML « classique ». Ce système de décorateur est en fait mis en place pour contourner une grosse faiblesse du langage HTML. Le problème avec HTML est que l’on ne peut pas créer de composant ayant son propre contexte d’exécution. Vous ne pouvez pas créer un composant qui contient du JavaScript en interne, propre au composant et ayant pour scope le composant.
Si vous regardez du côté du framework Flex, vous verrez que l’on peut créer ce genre de composant indépendant, dans un fichier à part. Ayant mangé plusieurs années de développement Flex au quotidien, je peux vous assurer que cela vous facilite la tâche lorsque vous voulez créer des composants réutilisables, en encapsulant son aspect graphique et son comportement (code).
Si vous lisez ce draft W3C sur les « Web Components », vous verrez que c’est le genre de chose qu’ils essaient de reproduire avec ce qu’ils appelent les « Custom Elements« :
Introduction to Web Components
Cela permet aussi de s’échanger facilement des composants / librairies et de les intégrer à un projet. Bref, vous l’avez compris, j’ai adoré le système de composant lorsque je faisais du développement Flex et si ce système avait été adopté dans le monde HTML dès le départ, je pense que le développement web ressemblerait moins à un champ de bataille de librairies :).
Alors pourquoi Windows y est allé de ses propres attributs? Et bien en fait, ils n’avaient pas vraiment le choix, étant dans un contexte « web », il leur a fallu « broder » avec des propriétés dynamiques qui peuvent être résolues à la volée. Ça parait assez moche au départ mais c’est plutôt malin, car le contexte d’exécution du composant est cloisonné dans la propriété « winControl » de l’élément DOM.
Voilà, ça en est fini pour mon analyse personnelle, vous pouvez bien sûr utiliser les commentaires pour donner votre avis!