Développer des apps Windows 8 en Javascript






24 Octobre 2013 - @SoftShake

Fabien NIcollet


  • @fnicollet / fnicollet@gmail.com
  • Dév Flex / AIR / web / Java @ Business Geografic @ Lyon
  • flex-tutorial.fr: 1000+ tutos, 700KVU
  • html5-tutorial.fr: 60+ tutos
           

Windows 8


Windows 8 : grand public = Modern UI + old school W7
Windows RT : tablettes tactiles ARM = Modern UI

Les applications


Design épuré et consistant, proche de la 360

Windows StoreÉcran de démarrage de Windows 8

Installation simplifiée
Promotion et rémunération

Les "Charms"

Fonctionnalités disponibles en permanence 

OH SNAP!


2 applications à la fois

Démarrer, arrêter


Démarrer = Modern UI
Arrêter = Charm Settings
Possibilité d'installer une application qui rétablit le bouton démarrer
Version 8.1 rétablit le bouton démarrer

Opportunités

450 millions de Windows 7 
500 millions de machines compatibles Windows 8
1,25 milliards d’utilisateurs Windows dans le monde
Store relativement vide
70% des revenus pour le développeur


Opinion personnelle

Store pas au niveau, mieux en 8.1

Découvrabilité quasi-nulle
Portail web réservé aux apps sponso
App flood war


OPINION PERSONNELLE

L'OS de tes parents = consommateurs potentiels

Opinion personnelle

Hybride difficile à expliquer
Outlook != Outlook RT ??

OPINIon personnelle


Plateforme peu populaire

UI Guidelines difficiles à respecter

Pas de compatibilité avec Windows *Phone* 8 pour les développements JS

Pas un smartphone mais pas forcement un desktop ... trop de devices?


skills


Plus besoin d'être un "développeur Microsoft"

  • Devs "web" -> HTML / JS / CSS
  • Devs WPF / Silverlight -> XAML / C++ / C# / VB
  • Devs DirectX -> C++ natif + HLSL

schema qui va bien


APIs

HTML5 supporté par IE10

Windows Runtime : API native de l’OS
WinJS : composants, styles et utils

APIs Html5


Layout / Style / CSS

CSS GRID ALIGNEMENT


"The easiest way to vertically center something in CSS is to close your laptop and go to the bar." @bleikamp

-ms-grid-row-align: "center" 

CSS MEDIA QUERIES


@media screen and (min-width: 800px) and (max-width: 1024px) { #rootGrid { -ms-grid-columns: 40px 1fr 40px; } }

@media (-ms-view-state: fullscreen-landscape) { #f { display: none; } }

CSS CALC


 .banniere {
  position:absolute;
  width: calc(100% - 80px);
  padding: 6px;
  text-align: center;
}

APIS HTML5



HTML5 WEB WORKERS

Exécution de JS en dehors du UI Thread

HTML5 WEB WORKERS


function displayPrime(e) { 
    primesCount += e.data; 
}...  primes1 = new Worker('js/primes.js'); 
 primes2 = new Worker('js/primes.js'); 
 primes1.postMessage(2); 
 primes1.onmessage = displayPrime; 
 primes2.postMessage(3); 
 primes2.onmessage = displayPrime; 

Appel d'un Web Worker par passage d'un script JS
Communication bi-directionnelle par message
postMessage - onMessage

HTML5 WEB WORKERS

(function () { 
    "use strict"; 
 
    var primes = new Array(); 
    var i; 
 
    self.onmessage = function (e) { 
        i = parseInt(e.data); 
 
        //2 is a prime 
        if (i === 2{ 
            postMessage(1); 
            i += 2; 
        } 
 
        calculatePrimes(); 
    }; 
 
    function isPrime(number) { 
        //Check each number n=1 to sqrt(number) and see if any divide evenly into number, if so then not a prime! 
        for (var n = 2; n <= Math.sqrt(number) ; n++) { 
            if (number % n === 0{ 
                return; 
            } 
        } 
        primes.push(number); 
        postMessage(1); //Tell the UI thread we found a prime. 
    } 
 
    function calculatePrimes() { 
        isPrime(i); 
        i += 2//Each thread handles every other possible number. 
        setTimeout(calculatePrimes, 0); 
    } 
})(); 
js/primes.js

HTML5 APP CACHE



ECMASCRIPT 5


Function.prototype.bind
fun.bind(thisArg[, arg1[, arg2[, ...]]])  
Equivalent jQuery : $.proxy(fun, thisArg)

Array.forEach

AUDIO & VIDeO TAGS

Audio tag
 <audio id="mediaPlayer" 
	autoplay 
	controls="controls" 
	msAudioCategory="backgroundcapablemedia">
</audio>
set src

var mediaPlayer = document.getElementById("mediaPlayer");
mediaPlayer.src = "http://someurl.com/somesong.mp3";
Vidéo

 <video src="http://www.someurl.com/clip.mp4" controls/>

WINJS


WinJS + HTML5 (selectors) = peut remplacer jQuery

Environnement de dev


+

+

WinDOWS 8 DEV TOOLS

VS & Blend gratuits : http://bit.ly/Win8Download
Windows 8 en version d'évaluation

ENVIRONNEMENT D'EXECUTION


IE10 dans un processus dédié
Bridge avec les APIs natives WinRT

NAVIGATION PATTERNS


Hub / Section / Detail (ex. Windows Store)

NAVIGATION PATTERNS


Flat (ex. Browser)

SEMANTIC ZOOM


Ctrl + molette ou pinch
Résumé / navigation rapide

UI PATTERNS


Header Menu, Back button
Top app bar (navigation)
Bottom app bar (controls, sort, filter)
Apparition par swipe

VIEW STATES


Gestion obligatoire de tous les modes
Mode snapped pas forcement fonctionnel (ex. WStore)

5 templates


Structure commune

  • default.html, la page d’accueil de l’application
  • default.js qui permet de scripter l’application au démarrage
  • default.css pour les styles de l’application.
  • package.appxmanifest qui décrit le package d’application pour Windows (un peu comme le fichier *-app.xml pour Adobe AIR).
  • Les logos / splashscreens nécessaires pour une application Metro

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>

BLANK APPLICATION


Minimaliste
0 UI
0 data

Navigation Application


Un PageNavigator et une Page

GRID APPLICATION

Navigation Template + UI + données de test
Liste avec navigation hierarchical


SPLIT APPLICATION

Navigation Template + Detail page

FIXED LAYOUT APPLICATION


Blank en dimensions fixes
Destiné aux jeux

APPLICATION LIFECYCLE

Le système peut décider de kill l'application, idem Android

DEFAULT.JS

(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: launched. Initialize
            } else {
                // TODO: reactivated from suspension.
            }
            WinJS.UI.processAll();
        }
    };
    app.oncheckpoint = function (eventObject) {
        // TODO: about to be suspended. Save any state.
    };
    app.start();
})();

METHODES SYSTEME

WINJS.APPLICATION.START

Démarre l'"event loop" de l'application
Permet de capter les événements système

WINJS.APPLICATION.oncheckpoint

Appelé juste avant la mise en arrière-plan (ex. alt-tab)
Etat de l'app à sauvegarder car l'app devient killable

WINJS.APPLICATION.ONACTIVATED

Appelé lors du lancement de l'application ou lors de la restauration au premier plan

TRANSITION RUN-SUSP-TERMINATED

Background Audio disponible avec API spécifique

WinJS.UI.processAll


Demande à tous les composants d'être rendus / interprétés
Va transformer tous les composants annotés "data-win-*"
Transforme le DOM annoté en composant "natif"

2 étapes
data-win-control : instanciation des composants
data-win-options: set d'une map d'options sur le composant

Décorateur Web Components / Angular-like

Modèle de navigation (SPWA)


De base, l'application ne sais pas faire
MS fournit une API / implémentation JS
WinJS.UI.Fragments : bas niveau
WinJS.Navigation: gère la stack
PageControlNavigator: Charg. / déchargement des Pages
WinJS.UI.Pages: haut niveau, utilisée dans les templates

1 page = 1 HTML +1 JS + 1 CSS

TEMPLATE NAVIGATION


  • default.html : contient un simple div associé à un composant PageControlNavigator qui pointe sur « pages/home/home.html »
  • js/default.js : contient un code de base permettant de démarrer l’application
  • css/default.css : styles de base
  • pages/home contient une « page », composée des fichiers home.html, home.js et home.css. C’est la structure de base pour une page
  • js/navigator.js : contient l’implémentation du composant PageControlNavigator

COMPOSANT PAGECONTROLNAVIGATOR

default.html
<div id="contenthost" data-win-control="appName.PageControlNavigator"
    data-win-options="{home: '/pages/items/items.html'}">
</div>
décoration data-win-control="appName.PageControlNavigator"
data-win-option pour définir une page de démarrage

PAGE DE BASE

<!DOCTYPE html>
<html>
<head>
    <!--... typical HTML header and WinJS references omitted -->
    <link href="/css/default.css" rel="stylesheet">
    <link href="/pages/home/home.css" rel="stylesheet">
    <script src="/pages/home/home.js"></script>
</head>
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">Welcome to NavApp!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
            <p>Content goes here.</p>
        </section>
    </div>
</body>
</html>

DÉFINITION D'uNE PAGE EN JS

 (function () {
    "use strict";
WinJS.UI.Pages.define("/pages/somePage/somePage.html", {
        ready: function (element, options) {
        },
updateLayout: function (element, viewState, lastViewState) {
            // TODO: Respond to changes in viewState.
        },
unload: function () {
            // TODO: Respond to navigations away from this page.
        }
    });
})();

Utilisation de l'API WinJS.UI.Pages
Identifiant de page = Chemin vers la page

NAVIGATION VERS UNE PAGE


API WinJS.Navigation

WinJS.Navigation.navigate("/pages/groupDetail/groupDetail.html") 

WINJS.PROMISE API

Objet à manipuler pour communication asynchrone
hooks multiples then(complete, error, progress)
then() renvoie une autre Promise -> Chaining API

  WinJS.xhr({ 
	url: "https://api.soundcloud.com/tracks.json" 
 }).then(function (response) {
	console.log(response.responseText);
}, function (ex){
	// report error
});

i18n

Mécanisme d'i18n intégré à Visual Studio, format "standard" 
Demandez à vos utilisateurs?

i18n (IMPL.)


Utilisation au format Data Binding
attribut data-win-res

 <button id="addCommentButton" type="button" style="margin-left:20px;" data-win-res="{textContent: 'add_a_comment'}"></button>

WINJS icons

WinJS.UI.AppBarIcon (http://goo.gl/xugv2)

HTML CONTROLS


HTML CONTROLS STYLED


COMPONENTS


EX. LISTVIEW

ListView = dataSource + itemRenderer + Layout

EX. LISTVIEW

Template
 <div id="tpl" data-win-control="WinJS.Binding.Template" style="display:none">
	<div class="someText">
		<img src="#" class="someImg" data-win-bind="src: picture" />
		<div data-win-bind="innerText: textProp"></div>
	</div>
</div>
déclaration ListView

 <div id="listView" data-win-control="WinJS.UI.ListView"
	data-win-options="{
		itemDataSource: myData.dataSource,
		itemTemplate : tpl,
		selectionMode: 'none',
		tapBehaviour: 'none',
		swipeBehaviour: 'none',
		layout: {type: WinJS.UI.GridLayout}
	}"
></div>

DATA BINDING

Instance du composant (decorator) dispo dans "winControl"

 var listView = document.getElementById("list").winControl;listView.addEventListener("selectionchanged", onSelChange);
Datasources prêtes pour le data-binding
WinJS.Binding.List
var data = JSON.parse(result.responseText);
var list = new WinJS.Binding.List(data);
La propriété "dataSource" de List est Bindable

var listView = document.querySelector(".tracklist");
WinJS.UI.setOptions(listView.winControl, {
	layout: new WinJS.UI.ListLayout(),
	itemDataSource: list.dataSource
});

LIBRAIRIE UI


EX. APPBAR

WinJS.UI.AppBar
 <div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="{sticky:true}">
	<button data-win-control="WinJS.UI.AppBarCommand" 
		data-win-options="{section:'selection', icon:'next', label: 'Next'}"
		></button>
	<button data-win-control="WinJS.UI.AppBarCommand" 
		data-win-options="{section:'selection', icon:'previous', label: 'Previous'}"
		></button>
	<button data-win-control="WinJS.UI.AppBarCommand" 
		data-win-options="{section:'selection', icon:'emoji2', label: 'Brag', type: 'flyout', flyout: 'bragFlyout'}"
		></button>
</div>
WinJS.UI.Menu

 <div id="bragFlyout" data-win-control="WinJS.UI.Menu">
	<button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'collectionMenuItem',label:'Collection'}">
	</button>
	<button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'marketplaceMenuItem',label:'Marketplace'}">
	</button>
</div>

EX. PRISE DE PHOTO

 document.getElementById("photo").addEventListener("click", function (e) {
  var camera = new Windows.Media.Capture.CameraCaptureUI();
  var mode = Windows.Media.Capture.CameraCaptureUIMode.photo;
  camera.captureFileAsync(mode).then(function (file) {
    if (file) {
      _photo = file;
	  var dtm = Windows.ApplicationModel.DataTransfer.DataTransferManager;
      dtm.showShareUI();
    }
  });
});
Récupération des interfaces natives de prise / partage de photo
Idem iOS / Android

WINJS FILE API

Possibilité d'écrire dans les "Windows.Storage.KnownFolders" (documentsLibrary, musicLibrary, picturesLibrary, videosLibrary)
Windows.Storage.KnownFolders.documentsLibrary.createFileAsync("sample.txt",Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) {
        sampleFile = file;
    });
Ecrire du texte dans un fichier

 Windows.Storage.FileIO.writeTextAsync(sampleFile, "Swift as a shadow").then(function () {
    // Add code to do something after the text is written to the file
});
Lecture, écriture, streaming, ...

ROAMING


Similaire aux cookies
préférences et personnalisations
sync across (Windows 8) devices

 var applicationData = Windows.Storage.ApplicationData.current;
// key-value data store
var roamingSettings = applicationData.roamingSettings;
roamingSettings.values["exampleSetting"] = "Hello World";
// écriture de File
var roamingFolder = applicationData.roamingFolder;
soumis à un quota global

GEOLOCATION



var loc = new Windows.Devices.Geolocation.Geolocator();
loc.getGeopositionAsync().then(getPositionHandler, errorHandler);
Peut aussi utiliser l'API HTML5 (GPS?)

CONTRACTS

Liaison entre les capacités natives de Windows 8 et son app
  • Search
  • Share
  • Print (!)
  • Settings
  • Backgound tasks (download)
  • Background Audio (Media Player)
  • App to App Picking
  • ...


EX. SEARCH


EX. SEARCH (impl.)

L'utilisateur lance une recherche sur votre app
 var appModel = Windows.ApplicationModel;
appModel.Search.SearchPane.getForCurrentView().onquerysubmitted = function (args) { 
	WinJS.Navigation.navigate(searchPageURI, args); 
};
Récupération de suggestion avec deferral

appModel.Search.SearchPane.getForCurrentView().onsuggestionsrequested = function (eventObject) {
  var queryText = eventObject.queryText;
  var suggestions = eventObject.request.searchSuggestionCollection;
  var deferral = eventObject.request.getDeferral();
  window.soundCloudService.getTracks({ q: queryText, limit: 5, suggestion: true }, function (suggestResults) {
	  suggestResults.forEach(function (item) {
		suggestions.appendQuerySuggestion(item.label);
	  }, function error(e) {
		//console.log("on error");
	  });
    deferral.complete();
  });
};

EX. SHARE

Abonnement à l'action Share de l'utilisateur
var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
dataTransferManager.addEventListener("datarequested", onDataTransferRequest);
Passage d'un élément à Share (lien, file, image)

     function onDataTransferRequest(e) {
        var request = e.request;
        request.data.properties.title = trackToShare.title + " " + "by" + " " + trackToShare.user.username + " on SoundCloud";
        request.data.properties.description = "hey !";
        request.data.setUri(new Windows.Foundation.Uri(trackToShare.permalink_url));
    };

TILES

Nombreux templates disponibles

TILES (IMPL.)


Chaque template est un document XML
Parcours du DOM et assignation des nods (src, innerText)
 var squareTileXml = Notifications.TileUpdateManager.getTemplateContent(Notifications.TileTemplateType.tileSquareImage);
var squareTileImageAttributes = squareTileXml.getElementsByTagName("image");
squareTileImageAttributes[0].setAttribute("src", tileImageSrc);
Fire d'une notification par Windows.UI.Notifications.TileUpdateManager

APP TERMINEE, GO WINDOWS STORE!


but wait ...

Icons
SplashScreen
Mode Snap, portrait
UX Guidelines
Comportement souris / touch
Privacy Policy (in & out app)
Langue par défaut
Réserver un nom / nom de package
Kit de certification
Compte développeur payant (30€, one shot)
Certification (1 semaine min.)
... (http://goo.gl/M5NvK)

...


RETOUR d'XP

Debugging mystérieux


RETOUR D'XP

*Excellente* documentation / samples / forums

Forum Building Windows Store apps with HTML5/JavaScript
Sample : HTML ListView item templates sample
Documentatio WinJS.UI.ListView

RETOUR D'XP


Développement différent d'une application iOS / Android

Utilisation nomade / déconnectée?
Vraie concurrence avec le web
Fonctionnalités type Print
Support principal desktop -> Utilité de l'app? (ex. Mix-IT)

EX. Application MIx-it

  1. Créer un projet de type Navigation Application
  2. XHR le flux JSON mix-it.fr/api/talks?details=true 
  3. Ajouter un composant List en première page
  4. Bind le tableau de session à la List
  5. Créer un template pour la liste
  6. Au click sur la liste, naviguer vers une nouvelle page
  7. Afficher le détail de la session et la liste des tags

XHR

 WinJS.xhr({ url: "http://www.mix-it.fr/api/talks?details=true" }).then(this.onResponse.bind(this)); 

LISTView

 <div id="sessionList" data-win-control="WinJS.UI.ListView" style="height:100%;"></div>

ONRESPONSE

 onResponse : function (response) {
            var sessions = JSON.parse(response.responseText);
            console.log(sessions);
            var list = new WinJS.Binding.List(sessions);
            var sessionList = document.getElementById("sessionList").winControl;
            var tpl = document.getElementById("sessionTemplate");
            WinJS.UI.setOptions(sessionList, {
                itemDataSource: list.dataSource,
                itemTemplate: tpl,
                oniteminvoked: this.onItemInvoked
            });
        },

oniteminvoked

 onItemInvoked: function (args) {
            args.detail.itemPromise.done(function a(item) {
                WinJS.Navigation.navigate("/pages/session/sessionPage.html", { selectedSession: item.data });
            });
        }

RESSOURCES

Free ebook: Programming Windows 8 Apps with HTML, CSS, and JavaScript

Dev Center

Site du Zéro

HTML5 Tutorial :)

Développer des apps Windows 8 en Javascript

By Fabien Nicollet

Développer des apps Windows 8 en Javascript

  • 7,875