Les derniers articles sur le dev Windows 8 Metro étaient plutôt descriptifs. Pour celui-là, on commence enfin à coder avec le framework WinJS. On va commencer doucement avec l’affichage d’une simple liste. Pour faire encore plus simple, on la peupler avec des données bidons. Plus tard, on verra comment la peupler à partir d’un appel serveur (XHR), ce qui revient au final au même.
La donnée
La donnée est une liste de musées, que j’ai emprunté depuis un tutorial jQuery de @nicolasgans. Une simple liste au format JSON, avec quelques informations pour chaque musée:
{ "musees" : [ { "id" : "0", "nom" : "Musée d'art moderne de la Ville de Paris", "adresse" : "11, avenue du Président Wilson - Paris 16e", "site" : "mam.paris.fr", "image" : "art_moderne.jpg", "texte" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id" : "1", "nom" : "Musée Bourdelle", "adresse" : "16, rue Antoine-Bourdelle - Paris 15e", "site" : "bourdelle.paris.fr", "image" : "bourdelle.jpg", "texte" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." },
Création d’un projet Windows 8 Metro
Ce tutorial assume bien sûr que vous avez une VM avec Windows 8 d’installé ainsi que le SDK + Visual Studio 2011 Express Beta:
Installation de Windows 8 Consumer Preview sur VMware Workstation 8
Introduction au développement HTML / JavaScript / CSS pour Windows 8
C’est parti, File > New Project > JavaScript > Windows Metro style. Choisissez le template « Blank Application »:
Un petit récap sur les différents templates à votre disposition:
Windows Metro – Les templates pour application JavaScript (Grid, Navigation, Split, …)
Patientez un peu et votre projet est créé. Vous devriez avoir le contenu du fichier « default.js » sous les yeux. Lancez donc l’application pour vous assurer du bon fonctionnement avec le bouton « Play » réglé sur Local Machine pour avoir directement le rendu dans Windows Metro.
Votre application va se lancer et afficher « Content goes here » sur fond noir.
Ajout d’un composant ListView
Sortez donc de l’application qui vient de se lancer (Alt-Tab pour switcher rapidement entre le dev et l’application) et revenez dans Visual Studio. Depuis l’arbre « Solution Explorer » à droite, ouvrez le fichier « default.html ». Voici son contenu:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>SimpleList</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> <!-- SimpleList references --> <link href="/css/default.css" rel="stylesheet"> <script src="/js/default.js"></script> </head> <body> <p>Content goes here</p> </body> </html>
On va remplacer le « Content goes here » par notre liste. Contrairement au HMTL « classique », les listes sous Windows 8 de sont pas de simples UL / LI. Au lieu de cela, on va créer un conteneur, un div:
<body> <div></div> </body>
A ce conteneur, on va associer le composant ListView qui va venir « décorer » le div et lui donner son comportement liste. Pour cela, on va référencer le nom complet du composant ListView : WinJS.UI.ListView. On va indiquer ce décorateur dans l’attribut « data-win-control »:
Voilà, vous avez votre composant ListView. Donnez lui un id « simpleList » pour pouvoir le référencer plus tard:
<div id="simpleList" data-win-control="WinJS.UI.ListView"></div>
On va maintenant l’alimenter avec des données.
Lier une ListView avec des données grâce à WinJS.Binding.List
Notre donnée est de base dans un tableau JSON. Cependant, on va faire passer ce tableau JSON dans un objet du framework WinJS qui se nomme « WinJS.Binding.List ». Celui-ci va encapsuler la donnée et vous donnez des méthodes d’accès plus pratiques (insérer un élément à un index, supprimer un élément, etc.).
Le gros avantage de cette manipulation et que vous allez pouvoir créer une liaison dynamique entre votre composant graphique (WinJS.UI.ListView) et votre donnée (WinJS.Binding.List). Ainsi, si la List est modifiée (ajout / suppression d’élément), le composant graphique sera automatiquement mis à jour. Du vrai MVC comme dirait l’autre :).
Pour les développeurs Flex, c’est l’équivalent de la classe ArrayCollection par rapport à Array.
Retournez dans le fichier « default.js » et juste après la déclaration de la variable app, déclarez votre tableau de musées:
(function () { "use strict"; var app = WinJS.Application; var museums = [ { "id": "0", "nom": "Musée d'art moderne de la Ville de Paris", "adresse": "11, avenue du Président Wilson - Paris 16e", "site": "mam.paris.fr", "image": "art_moderne.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "1", "nom": "Musée Bourdelle", "adresse": "16, rue Antoine-Bourdelle - Paris 15e", "site": "bourdelle.paris.fr", "image": "bourdelle.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "2", "nom": "Musée Carnavalet", "adresse": "23, rue de Sévigné - Paris 3e", "site": "carnavalet.paris.fr", "image": "carnavalet.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "3", "nom": "Catacombes", "adresse": "1, avenue du Colonel Henri Rol-Tanguy - Paris 14e", "site": "catacombes.paris.fr", "image": "catacombes.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "4", "nom": "Musée Cernuschi", "adresse": "7, avenue Vélasquez (accès par le 111-113, bd Malesherbes) - Paris 8e", "site": "cernuschi.paris.fr", "image": "cernuschi.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "5", "nom": "Musée Cognacq-Jay", "adresse": "8, rue Elzévir - Paris 3e", "site": "cognacq-jay.paris.fr", "image": "cognacq_jay.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "6", "nom": "Maison de Balzac", "adresse": "47, rue Raynouard - Paris 16e", "site": "balzac.paris.fr", "image": "maison_balzac.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "7", "nom": "Crypte archéologique du parvis de Notre-Dame", "adresse": "Place Jean-Paul II - Parvis de Notre-Dame - Paris 1er", "site": "crypte.paris.fr", "image": "notre_dame.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "8", "nom": "Petit Palais", "adresse": "Avenue Winston Churchill - Paris 8e", "site": "petitpalais.paris.fr", "image": "petit_palais.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "9", "nom": "Maison de Victor Hugo", "adresse": "6, place des Vosges - Paris 4e", "site": "musee-hugo.paris.fr", "image": "victor_hugo.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "10", "nom": "Musée de la Vie Romantique - Hôtel Scheffer-Renan", "adresse": "16, rue Chaptal - Paris 9e", "site": "vie-romantique.paris.fr", "image": "vie_romantique.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." }, { "id": "11", "nom": "Musée Zadkine", "adresse": "100 bis, rue d’Assas - Paris 6e", "site": "zadkine.paris.fr", "image": "zadkine.jpg", "texte": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis nibh, aliquam in semper quis, accumsan vitae est. Nunc sed urna nunc. Donec congue justo sed purus molestie dignissim. Cras posuere tempor sem quis sodales. Vestibulum varius fringilla accumsan. Suspendisse commodo lectus id justo viverra ultrices. Nullam non orci arcu, vel tincidunt velit. Suspendisse semper suscipit magna. Nullam tempor turpis at justo pulvinar venenatis ut eu elit. Phasellus lacus justo, venenatis id vestibulum at, commodo vehicula arcu. Morbi non ipsum neque." } ]; app.onactivated = function (eventObject) {
On va ensuite rajouter une fonction dans laquelle on va initialiser la List. On appelle celle-ci au moment de l’exécution de l’application, juste après l’appel à WinJS.UI.processAll:
var initList = function () { }; app.onactivated = function (eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) { } else { } WinJS.UI.processAll(); initList(); } };
On crée un objet de type List auquel on passe museums dans le constructeur:
var initList = function () { var list = new WinJS.Binding.List(museums); };
Lier la List et la ListView
Pour lier les 2 (opération appelée « Binding »), on va récupérer une référence vers la ListView et surtout vers son attribut « winControl » qui contient l’instance de WinJS.UI.ListView:
var initList = function () { var list = new WinJS.Binding.List(museums); var simpleList = document.getElementById('simpleList'); var listView = simpleList.winControl; };
Pour lier les 2, on utilise la méthode WinJS.UI.setOptions:
http://msdn.microsoft.com/en-us/library/windows/apps/hh440978.aspx
Celle-ci nous permet de passer plusieurs propriétés à la fois à un composant. Dans notre cas, on a qu’une propriété à modifier « itemDataSource ». Le premier argument est le composant à cibler, le deuxième, une map JSON des assignations à faire:
WinJS.UI.setOptions(listView, { itemDataSource: list.dataSource });
Notez que l’on ne passe pas la List en elle même mais son contenu, représenté par la propriété « dataSource ». Relancez l’application pour voir le résultat.
Note: Vous pouvez couper le debug et le relancer ou bien recharger directement l’application avec Ctrl+Shift+R
Ce n’est pas vraiment le résultat escompté, la liste affiche le contenu brut. Mais si vous cliquez dessus, vous pouvez déjà voir que vous avez automatiquement le comportement et le style de Windows Metro.
Lier la ListView à un Template
Comme je l’ai dit dans l’article en introduction à Windows 8, vous allez effectuer le rendu de chaque élément de la liste grâce à un template. Un template est un « modèle » de rendu qui sera réutilisé par les éléments. Seul les données sont dynamiques.
On va créer notre template dans le fichier default.html. Celui-ci est un élément DOM classique avec un attribut data-win-control égal à WinJS.Binding.Template:
<body> <!-- templates --> <div id="itemTemplate" data-win-control="WinJS.Binding.Template"> <div class="item-image-container"> <img class="item-image" src="#"/> </div> <div class="item-overlay"> <h4 class="item-title"></h4> </div> </div> <!-- interface --> <section> <div id="simpleList" data-win-control="WinJS.UI.ListView"></div> </section> </body>
Grâce aux attributs « data-win-bind », on va réaliser une liaison dynamique entre l’élément de donnée et le DOM HTML:
<div id="itemTemplate" data-win-control="WinJS.Binding.Template"> <div class="item-image-container"> <img class="item-image" src="#" data-win-bind="src: image"/> </div> <div class="item-overlay"> <h4 class="item-title" data-win-bind="innerHTML : nom"></h4> </div> </div>
L’attribut « data-win-bind » prend soit une assignation simple comme ci-dessus ou un objet JSON pour faire un Binding multiple.
On va maintenant lier la liste au Template. Pour cela, on va réutiliser notre méthode setOptions utilisée plus haut et assigner la propriété « itemTemplate »:
var initList = function () { var list = new WinJS.Binding.List(museums); var simpleList = document.getElementById('simpleList'); var listView = simpleList.winControl; var template = document.getElementById('itemTemplate'); WinJS.UI.setOptions(listView, { itemDataSource: list.dataSource, itemTemplate: template }); };
Relancez l’application (Ctrl+Shift+R):
Ok, j’ai oublié de vous donner les images qui vont bien, vous pouvez les télécharger ici:
http://html5-tutorial.fr/wp-content/uploads/2012/05/ParisCulture_AdobeLive_2012.zip
Mettez toutes les images directement à la racine du projet pour l’instant et relancez:
C’est mieux :).
Un peu de style
Avant de terminer ce tutorial, on va donner un peu de style à notre ListView. Pour faire simple, on va faire ça directement dans un tag <style> dans default.html:
<style> section { display: -ms-grid; -ms-grid-columns: 1fr; -ms-grid-rows: 1fr; } #simpleList .win-item { -ms-grid-columns: 1fr; -ms-grid-rows: 1fr 60px; display: -ms-grid; height: 180px; width: 220px; background: white; outline: rgba(0, 0, 0, 0.8) solid 2px; } #simpleList .win-item .item-image-container { -ms-grid-columns: 1fr; -ms-grid-rows: 1fr; -ms-grid-row: 1; display: -ms-grid; padding: 4px; -ms-transition: opacity ease-out 0.2s, -ms-transform ease-out 0.2s; -ms-transform: scale(1.0, 1.0); } #simpleList .win-item .item-image { -ms-grid-row: 1; -ms-grid-column-align: center; -ms-grid-row-align: center; max-height: 90px; } #simpleList .win-item .item-overlay { -ms-grid-row: 2; padding: 3px 15px 2px; background-color: rgba(0, 0, 0, 0.8); } #simpleList .win-item .item-overlay .item-title { overflow: hidden; color: white; } </style> </head>
Et voilà le résultat:
Conclusion
Vous êtes arrivé à la fin de ce tutorial et votre première liste Windows 8 Metro fonctionne, c’est déjà ça :).
Ce tutorial avait surtout pour but de vous introduire au fonctionnement des composants UI sous Windows 8 et au système de Template. Le reste des composants fonctionne sur le même modèle, donc si vous avez compris ce que vous venez de réaliser, vous n’aurez aucun problème à comprendre le reste :).
Ping : Windows Metro – Clouder! (3) – Afficher une liste de titre depuis SoundCloud | HTML5 Tutorial
tu as petard avec l’affichage de ta page et du code