530 likes | 636 Views
Windows Store Apps mit HTML & JS entwickeln. Christian Moser Software Architekt und UX Designer, Zühlke moc@zuehlke.com | @ christian_moser | wpftutorial.net. Ziele. Sie wissen wann HTML/JS/CSS die geeignete Entwicklungsplattform für eine App ist .
E N D
Windows Store Apps mitHTML & JS entwickeln Christian Moser Software Architekt und UX Designer, Zühlke moc@zuehlke.com | @christian_moser | wpftutorial.net
Ziele Siewissenwann HTML/JS/CSS die geeigneteEntwicklungsplattformfüreine App ist. Siekennen die grundlegendenKonzepteeiner JavaScript App Siesind in der Lageselbereineeinfache HTML/JS App zuentwickeln.
Agenda HTML/JS als App-Sprachen Aufbaueiner HTML/JS App Classes and Namespaces Application Lifecycle und State handling Pages und Navigation Controls DataBinding Model-View-ViewModel Pattern Windows Runtime Third-Party Libraries
HTML/JS als App-Sprachen Teil 1
Motivation für HTML/JS als App-Sprachen? SiehabenbereitsErfahrungmit Web-Technologien Esbestehenbereits JavaScript-Komponenten Siemöchten Cross-Platform entwickeln Siebinden Services ein, die JSON-Datenliefern (können) Siesindheimlichverliebt in jQuery, require.js oder knockout?
Typische “Smells” der Webentwicklung Jeder Browser interpretiert Standards unterschiedlich HTML5 und CSS3 sindnur auf neustenBrowsernverfügbar JavaScript bietet out-of-the-box keineKonstruktefürgrössereMengen Code (z.B. Klassen, Namespaces, Interfaces, Module) JavaScript Performance istschlecht auf älterenBrowsern Sandboxed (keinDateizugriff, Office Integration,…)
Apps vs. Web-Pages Diverse Browser IE10 (mit all seinen Features) Multi- oder Single-Page Single-Page Sandboxed Plattform-Zugriff (WinRT) jQuery, Knockout, requireJS WinJS , jQuery, Knockout, requireJS
Demo Audio Explosion AudioExplosion Demo - IE Testdrive AudioExplosionApp
Aufbaueiner HTML/JS App Teil 2
Unser Werkzeugkoffer CSS 3 WinRT Knockout, jQuery, require.js Visual Studio Templates WinJS HTML 5 JavaScript 5
Visual Studio Templates Blank Navigation Split Grid Fixed Grid-View, List-View,Sample Data Grouped ListViewText-Wrapping Fixed size inViewBox Leere App navigator.js,Home-Page
App Lifecycle Activation Reasons Launched Secondary Tile Search target Share target Protocol activated File Save Picker File Open Picker File Activated Cached File Update Device activated Camera Settings Print task Running Suspending5s imHintergrund Activated Crashed Resumingin den Vordergrund NotRunning Suspended TerminatedSpeichermangel
Application Object var app = WinJS.Application, webApp= Windows.UI.WebUI.WebUIApplication, activation = Windows.ApplicationModel.Activation; app.onactivated = function (e) { if (e.detail.kind === activation.ActivationKind.launch) { // Initialization }}; Activating Suspending app.oncheckpoint = function (e) { // Save State and Navigation }; webApp.addEventListener("resuming", function (e) { // Restore State and Navigation }, false); Resuming
State and History beibehalten WinJS.Application.sessionState var app = WinJS.Application; varnav = WinJS.Navigation; app.oncheckpoint = function (e) { app.sessionState.history = nav.history; app.sessionState.myData = myData; }; webApp.addEventListener("resuming", function (e) {nav.history = app.sessionState.history;myData = app.sessionState.myData;}; ObjekteimSessioStatemüssen JSON serialisierbarsein.
Pages und Navigation Teil 4
Page definition <divid="contenthost"> </div> Page 1 WinJS.UI.Pages.define("/pages/home/home.html", { ready: function (element, options) { // Initialize page} }); varhost = document.getElementById('contentHost'); WinJS.Pages.render("/pages/home/home.html", host);
Navigation navigator.js WinJS.Navigation.onnavigating WinJS.Pages.render() Page 1 Page 2 Page 3 /pages/home/home.html /pages/add/add.html /pages/detail/detail.html WinJS.Navigation.navigate("/add/add.html"); WinJS.Navigation.back(1); WinJS.Navigation.forward();
Controls Teil 5
HTML5 Controls mit Win8-Styling <buttonclass="win-backbutton"></button> <select> <option>Option 1</option> <option>Option 2</option> <option>Option 3</option> </select> <inputtype="range"class="win-vertical"/> <progressclass="win-ring win-large"/>
WinJS Controls <divdata-win-control="WinJS.UI.Rating" data-win-options="{averageRating:3, maxRating:8}"> </div> <divdata-win-control="WinJS.UI.AppBar"> <buttondata-win-control="WinJS.UI.AppBarCommand" data-win-options="{label:'Add Album', icon:'add'}"> </button> </div> WinJS.UI.processAll(root);
Styling von Controls Part Pseudo-Element State Pseudo-Class Control Tag input[type=text]:hover::-ms-clear { background: purple; }
Classes and Namespaces Teil 6
Closures (function() { "usestrict"; // isolierter Code })(); Self-executing anonymous function verhindertNameskollisionenimglobalen Namespace.
Klassen in WinJS • varMyClass= WinJS.Class.define(function() { • // Constructor • }, { • // Members}, { • // Staticmembers}); • varmyObj= newMyClass();
Private Members und Properties var Person = WinJS.Class.define(function (firstname, lastname) { this._firstname = firstname; this._lastname = lastname; }, { fullName: { get: function () { returnthis.firstname + " " + this.lastname; } } }); varhans = new Person("Hans", "Muster"); console.log(hans.FullName); Private Members Wenn Name mit Underscore (“_”) beginnt Propertiesproperty: { get : function(){}, set : function(value){}}
Namespaces in WinJS WinJS.Namespace.define("Shape", { // Classes MyClass: WinJS.Class.define(function() {}), // Functions myFunction : function() {}, // Values myValue: 5 }); varmyInstance= newShape.MyClass();
Vererbung von Klassen var Person = WinJS.Class.define(function (firstname, lastname) { this._firstname = firstname; this._lastname = lastname; }, { fullname: { get: function () { returnthis.firstname + " " + this.lastname; } } }); var Employee = WinJS.Class.derive(Person, function(firstname, lastname, role) { this._firstname = firstname; this._lastname = lastname; this._role = role; }, { description : { get: function () { returnthis.fullname + " is a " + this._role; } } });
Data Binding Teil 7
Was ist an diesem Code schlecht? <buttonid="change-button"></button> <divid="person">Hans Muster</div> var button = document.getElementById('change-button'), person = document.getElementById('person'); button.addEventListener('clicked', function () { person.innerHTML= "Michael Müller"; }); Schlechttestbar Abhängigkeitvom Code zur View Kannnichtwiederverwendetwerden
Besserer Code <buttonid="change-button"></button> <divid="person">Hans Muster</div> Binding fehlt varviewModel = WinJS.Class.define(function() {}, { person = { name: "Hans Muster"}, changeName: function () { this.person.name = "Michael Müller"; } }); Gut testbar KeineAbhängigkeitzwischen Model und View Kannwiederverwendetwerden
Data Binding in WinJS Source • Target Binding Converter <divid="root"> <pdata-win-bind="textContent:name"></p> </div> var person = { name: "Hans Muster", }; DataContext varroot = document.getElementById("root");WinJS.Binding.processAll(root, person);
Bindable Wrappers varbindablePerson= WinJS.Class.define(function() { this._initObservable(); }, { name: { get: function () { returngetProperty("name"); }, set: function (value) { setProperty("name", value); } } }); WinJS.Class.mix(bindablePerson, WinJS.Binding.mixin); var person = { name: "Hans Muster", }; Erzeugtein Wrapper mit Properties und Change-Notification. varbindablePerson = WinJS.Binding.as(person);
Binding Mixin varBindablePerson = WinJS.Class.define(function() { this._initObservable(); }, { name : { get: function () { returngetProperty("name"); }, set: function (value) { returnsetProperty("name", value); } } }); WinJS.Class.mix(WinJS.Binding.mixin, BindablePerson);
Event Binding <divid="root"> <buttondata-win-bind="onclick:changeName"></button> <p data-win-bind="textContent:person.name"></p> </div> varViewModel= WinJS.Class.define(function () { },{ person: WinJS.Binding.as({ name: "Hans Muster"}), changeName: function () { this.person.name = "Michael Müller"; } }); ErrorValue is not supportedwithin a declarativeprocessingcontext. Errorpersonisundefined. WinJS.Binding.processAll(root, newViewModel());
Event Binding <divid="root"> <buttondata-win-bind="onclick:changeName"></button> <p data-win-bind="textContent:person.name"></p> </div> Löst Problem mitfalschemthiscallback. varViewModel= WinJS.Class.define(function () { this.changeName= this._changeName.bind(this); this.changeName.supportedForProcessing= true; },{ person: WinJS.Binding.as({ name: "Hans Muster"}), _changeName: function () { this.person.name = "Michael Müller"; } }); Markiert die Funktionals “bindable”. WinJS.Binding.processAll(root, newViewModel());
Binding Collections <divdata-win-control="WinJS.UI.ListView" data-win-bind="winControl.itemDataSource:persons:dataSource"></div> varviewModel = WinJS.Class.define(function () { var persons = newWinJS.Binding.List(); }, { add: function (name) { this.persons.push({ name: name }); } }); WinJS.Binding.processAll(root, viewModel); Die Binding-List ruftbeimHinzufügenfürjedes Item WinJs.Binding.as() auf.
Two Way Binding <inputtype="text"data-win-bind="value:searchTextBinding.Mode.twoway"/> searchText: { get: function () { returnthis._searchText; }, set: function (value) { this._searchText = value; this.doSearch(); } } http://mvvmstack.codeplex.com/
Data Templates Teil 8
Data Templates <divid="movie_template"data-win-control="WinJS.Binding.Template"> <divclass="movie"data-win-bind="style.backgroundImage:posterUrl"> <divclass="item-overlay"> <pdata-win-bind="textContent:title"></p> </div> </div> </div> Tipp: Das äusserte<div>istnichtTeil des Item-Templates. <divid="moviesList"data-win-control="WinJS.UI.ListView" data-win-bind="winControl.itemDataSource:movies.dataSource;" data-win-options="{itemTemplate:select('#movie_template')}"> </div>
Windows Runtime Teil 9
Windows Runtime (WinRT) Zugriff auf Windows 8 PlattformRessourcen Windows.Storage.KnownFolders.picturesLibrary .getFilesAsync().then(function (files) { files.forEach(function () { // Do something }); }); Windows.Data Windows.Devices Windows.Graphics Windows.Media Windows.Networking Windows.Security Windows.Storage Windows.Web Language Projection WinRT (COM) JavaScript Engine
Promises WinJS.xhr({ url: "http://www.google.com" }) .then(function (result) { out.innerText = "Success("+ result.responseText.length + " bytes)"; }, function (error) { out.innerHTML = "Failed to download: " + error.statusText; }, function (result) { out.innerText = "Downloading... " + result.readyState; });
Externe Libraries Teil 10
jQuery Rated: 3 $('#rating').bind('change', function () { $('#message').append($('<p>Rated: ' + this.winControl.userRating + '</p>')); }); $('#appBarCommand').click(function () { $('#message').append($('<p>Clicked!</p>')); $('#rating')[0].winControl.userRating = 5; }); http://code.jquery.com/jquery-2.0.0b2.js