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
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;
}
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);
if (i === 2) {
postMessage(1);
i += 2;
}
calculatePrimes();
};
function isPrime(number) {
for (var n = 2; n <= Math.sqrt(number) ; n++) {
if (number % n === 0) {
return;
}
}
primes.push(number);
postMessage(1);
}
function calculatePrimes() {
isPrime(i);
i += 2;
setTimeout(calculatePrimes, 0);
}
})();
js/primes.js
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
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 swipeVIEW 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)
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
});
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 (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
- Créer un projet de type Navigation Application
- XHR le flux JSON mix-it.fr/api/talks?details=true
- Ajouter un composant List en première page
- Bind le tableau de session à la List
- Créer un template pour la liste
- Au click sur la liste, naviguer vers une nouvelle page
- 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 :)