Creating windows store java script apps

41
CREATING WINDOWS STORE JAVASCRIPT APPS revealed by Eugene Zharkov / @2j2e

description

 

Transcript of Creating windows store java script apps

Page 1: Creating windows store java script apps

CREATING WINDOWS STORE JAVASCRIPTAPPS

revealed by Eugene Zharkov / @2j2e

Page 2: Creating windows store java script apps

PROTOTYPE

Check official UX guidlines

Page 3: Creating windows store java script apps

APP FLOWDo not forget about:

Form factorsApp contractsAll type of the tilesToast notificationsTouch firstAnimations

Page 4: Creating windows store java script apps

ASYNC PATTERNSYou should understand how:

to compose asynchronous codeto use promisesto chain and group promisesto code promise chains to avoid nestingto wrap non-promise values in a promiseto handle errors in a promise

Page 5: Creating windows store java script apps

PROMISESA promise is an object that represent a value that will be

available later.

Most API's are wraped to the promises.

All based on two methods .then and .done.

Page 6: Creating windows store java script apps

PROMISESA promise is an object that represent a value that will be

available later.Hilo\Hilo\imageQueryBuilder.js

if (this.settings.bindable) {// Create Hilo.Picture objects instead of returning StorageFile objects queryPromise = queryPromise.then(this._createViewModels);}

Page 7: Creating windows store java script apps

PROMISE CHAINvar whenImagesForTileRetrieved = queryBuilder.build(picturesLibrary).execute();whenImagesForTileRetrieved .then(Hilo.Tiles.createTileFriendlyImages) .then(this.getLocalImagePaths) .then(Hilo.Tiles.createTileUpdates) .then(queueTileUpdates);

Page 8: Creating windows store java script apps

PROMISE CHAIN

Page 9: Creating windows store java script apps

BIND FUNCTIONDefine funtion environment with .bind

Hilo\Hilo\Tiles\TileUpdater.js

var queueTileUpdates = this.queueTileUpdates.bind(this);

queueTileUpdates: function (notifications) { var self = this; notifications.forEach(function (notification) { self.tileUpdater.update(notification); });},

Page 10: Creating windows store java script apps

BIND FUNCTIONBind can receive additional parameters.

Hilo\Hilo\Tiles\createTileFriendlyImages.js

function createTileFriendlyImages(files) { var localFolder = applicationData.current.localFolder;

var copyThumbnailsToFolder = copyFilesToFolder.bind(null, files);

var whenFolderCreated = localFolder.createFolderAsync(thumbnailFolderName, creationCollisionOption.replaceExisting);

return whenFolderCreated .then(copyThumbnailsToFolder);}

Page 11: Creating windows store java script apps

GROUPING A PROMISEWhen you have non-sequential, asynchronous operations

that must all complete before you can continue a task, you canuse the WinJS.Promise.join

var whenFileIsOpen = targetFile.openAsync(fileAccessMode.readWrite);var whenThumbailIsReady = sourceFile.getThumbnailAsync(thumbnailMode.singleItem);var whenEverythingIsReady = WinJS.Promise.join({ opened: whenFileIsOpen, ready: whenThumbailIsReady });

Page 12: Creating windows store java script apps

HANDLING ERRORSUse .done at the end of method chain.

It doesn't return another promise and throw unhandledexpetions.

Page 13: Creating windows store java script apps

HANDLING ERRORSHilo\Hilo\crop\croppedImageWriter.js

var decoderPromise = getOrientation.then(function (retrievedProps) { exifOrientation = (retrievedProps.size !== 0) ? retrievedProps["System.Photo.Orientation"] : photoOrientation.normal;

}, function (error) { switch (error.number) { case Hilo.ImageWriter.WINCODEC_ERR_UNSUPPORTEDOPERATION: case Hilo.ImageWriter.WINCODEC_ERR_PROPERTYNOTSUPPORTED: exifOrientation = photoOrientation.normal; break; default: throw error; }});

Page 14: Creating windows store java script apps

MVPThe model represents the state and operations of businessobjects that your app manipulatesThe view (HTML and CSS) defines the structure, layout, andappearance of what the user sees on the screen. The viewmanages the controls on the page and forwards user eventsto a presenter classThe presenter contains the logic to respond to events,update the model and, in turn, manipulate the state of theview

Page 15: Creating windows store java script apps

MVP - VIEWHilo\Hilo\hub\hub.html

<div id="hub-image-template" data-win-control="WinJS.Binding.Template"> <div data-win-bind="style.backgroundImage: url.backgroundUrl; alt: name; className: className" class="thumbnail"> </div></div>

Page 16: Creating windows store java script apps

MVP - PRESENTER TESTHilo\Hilo.Specfications\specs\hub\ListViewPresenter.spec.js

describe("when snapped", function () { var el;

beforeEach(function () { var appView = {}; el = new Specs.WinControlStub(); el.winControl.addEventListener = function () { };

var listviewPresenter = new Hilo.Hub.ListViewPresenter(el, appView); listviewPresenter.setViewState(Windows.UI.ViewManagement.ApplicationViewState.snapped); });

it("the ListView should use a ListLayout", function () { expect(el.winControl.layout instanceof WinJS.UI.ListLayout).equal(true); });

});

Page 17: Creating windows store java script apps

PRESENTERSEach presenter is responsible for a specific part of the page (a

control)

One page-specific presenter (the mediator) coordinates theother presenters (control-specific) and receives forwarded

events

Page 18: Creating windows store java script apps

PRESENTERSHilo\Hilo\detail\detail.js

ready: function (element, options) {

var query = options.query; var queryDate = query.settings.monthAndYear; var pageTitle = Hilo.dateFormatter.getMonthFrom(queryDate) + " " + Hilo.dateFormatter.getYearFrom(queryDate); this.bindPageTitle(pageTitle);

var hiloAppBarEl = document.querySelector("#appbar"); var hiloAppBar = new Hilo.Controls.HiloAppBar.HiloAppBarPresenter(hiloAppBarEl, WinJS.Navigation, query);

var filmstripEl = document.querySelector("#filmstrip"); var flipviewEl = document.querySelector("#flipview");

var flipviewPresenter = new Hilo.Detail.FlipviewPresenter(flipviewEl); var filmstripPresenter = new Hilo.Detail.FilmstripPresenter(filmstripEl);

var detailPresenter = new Hilo.Detail.DetailPresenter(filmstripPresenter, flipviewPresenter, hiloAppBar, WinJS.Navigation); detailPresenter.addEventListener("pageSelected", function (args) { var itemIndex = args.detail.itemIndex; options.itemIndex = itemIndex; });

detailPresenter .start(options)

Page 19: Creating windows store java script apps

SEPARATING RESPONSIBILITIESHilo\Hilo\hub\hub.html

Hilo\Hilo\controls\HiloAppBar\hiloAppBar.html

<section id="image-nav" data-win-control="WinJS.UI.HtmlControl" data-win-options="{uri: '/Hilo/controls/HiloAppBar/hiloAppBar.html'}"></section>

<div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="{sticky: false}"> <button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'rotate', icon:'rotate', section: 'selection', disabled: true}" data-win-bind="{disabled: isCorrupt}" data-win-res="{winControl: {label:'RotateAppBarButton.Name'}}"> </button> <button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'crop', icon:'crop', section: 'selection', disabled: true}" data-win-bind="{disabled: isCorrupt}" data-win-res="{winControl: {label:'CropAppBarButton.Name'}}"> </button></div>

Page 20: Creating windows store java script apps

UPDATING TILESHilo\default.js

if (currentState.kind === activation.ActivationKind.launch) {

if (currentState.previousExecutionState !== activation.ApplicationExecutionState.terminated) {

// When the app is started, we want to update its tile // on the start screen. Since this API is not accessible // inside of Blend, we only invoke it when we are not in // design mode. if (!Windows.ApplicationModel.DesignMode.designModeEnabled) { var tileUpdater = new Hilo.Tiles.TileUpdater(); tileUpdater.update(); }}

Page 21: Creating windows store java script apps

UPDATING TILESHilo\Hilo\Tiles\TileUpdater.js

update: function () { // Bind the function to a context, so that this will be resolved // when it is invoked in the promise. var queueTileUpdates = this.queueTileUpdates.bind(this);

// Build a query to get the number of images needed for the tiles. var queryBuilder = new Hilo.ImageQueryBuilder(); queryBuilder.count(numberOfImagesToRetrieve);

var whenImagesForTileRetrieved = queryBuilder.build(picturesLibrary).execute(); whenImagesForTileRetrieved .then(Hilo.Tiles.createTileFriendlyImages) .then(this.getLocalImagePaths) .then(Hilo.Tiles.createTileUpdates) .then(queueTileUpdates);}

Page 22: Creating windows store java script apps

UPDATING TILESThe XML for a typical tile notification

<tile> <visual> <binding template="TileWideImageCollection"> <image id="1" src="ms-appdata:///local/thumbnails/thumbImage_0.jpg"/> <image id="2" src="ms-appdata:///local/thumbnails/thumbImage_1.jpg"/> <image id="3" src="ms-appdata:///local/thumbnails/thumbImage_2.jpg"/> <image id="4" src="ms-appdata:///local/thumbnails/thumbImage_3.jpg"/> <image id="5" src="ms-appdata:///local/thumbnails/thumbImage_4.jpg"/> </binding> <binding template="TileSquareImage"> <image id="1" src="ms-appdata:///local/thumbnails/thumbImage_0.jpg"/> </binding> </visual></tile>

Page 23: Creating windows store java script apps

PAGE NAVIGATIONHilo\Default.html

Hilo\Hilo\hub\hubPresenter.js

<div id="contenthost" data-win-control="Hilo.PageControlNavigator" data-win-options="{home: '/Hilo/hub/hub.html'}"></div>

itemClicked: function (args) {

// Get the Hilo.Picture item that was bound to the invoked image, // and the item index from the list view control. var picture = args.detail.item.data;

// Build the query that can find this picture within it's month group. var options = this.buildQueryForPicture(picture);

// Navigate to the detail view, specifying the month query to // show, and the index of the individual item that was invoked. this.nav.navigate("/Hilo/detail/detail.html", options);},

Page 24: Creating windows store java script apps

CREATING PAGESHilo\Hilo\hub\hub.js

Hilo.controls.pages.define("hub", {

ready: function (element, options) {

// Handle the app bar button clicks for showing and hiding the app bar. var appBarEl = document.querySelector("#appbar"); var hiloAppBar = new Hilo.Controls.HiloAppBar.HiloAppBarPresenter(appBarEl, WinJS.Navigation);

// Handle selecting and invoking (clicking) images. var listViewEl = document.querySelector("#picturesLibrary"); this.listViewPresenter = new Hilo.Hub.ListViewPresenter(listViewEl, Windows.UI.ViewManagement.ApplicationView);

// Coordinate the parts of the hub page. this.hubViewPresenter = new Hilo.Hub.HubViewPresenter( WinJS.Navigation, hiloAppBar, this.listViewPresenter, new Hilo.ImageQueryBuilder() );

this.hubViewPresenter .start(knownFolders.picturesLibrary) .then(function () { WinJS.Application.addEventListener("Hilo:ContentsChanged", Hilo.navigator.reload); });

Page 25: Creating windows store java script apps

SUPPORTING DIFFERENT LAYOUTSHilo\Hilo\PageControlNavigator.js

function PageControlNavigator(element, options) {

// . . .

window.onresize = this._resized.bind(this);

// . . . }, _resized: function (args) { if (this.pageControl && this.pageControl.updateLayout) { this.pageControl.updateLayout.call(this.pageControl, this.pageElement, appView.value, this._lastViewstate); } this._lastViewstate = appView.value;},

Page 26: Creating windows store java script apps

SUPPORTING DIFFERENT LAYOUTSHilo\Hilo\hub\hub.js

Hilo\Hilo\hub\listViewPresenter.js

updateLayout: function (element, viewState, lastViewState) { this.listViewPresenter.setViewState(viewState, lastViewState);},

setViewState: function (viewState) { this.lv.layout = this.selectLayout(viewState);},

selectLayout: function (viewState, lastViewState) {

if (lastViewState === viewState) { return; }

if (viewState === appViewState.snapped) { return new WinJS.UI.ListLayout(); } else { var layout = new WinJS.UI.GridLayout(); layout.groupInfo = function () { return listViewLayoutSettings; }; layout.maxRows = 3; return layout; }},

Page 27: Creating windows store java script apps

DATA BINDINGTemplates for group items and group headers in the normal

view.Hilo\Hilo\month\month.html

<div id="monthItemTemplate" data-win-control="WinJS.Binding.Template"> <div data-win-bind="style.backgroundImage: url.backgroundUrl; className: className"></div></div>

<div id="monthGroupHeaderTemplate" data-win-control="WinJS.Binding.Template"> <a class="monthLink" href="#"><span data-win-bind="innerHTML: title"></span> (<span data-win-bind="innerText: count"></span>)</a></div>

<div id="monthSnappedTemplate" data-win-control="WinJS.Binding.Template"> <span data-win-bind="innerHTML: title"></span> (<span data-win-bind="innerText: count"></span>) <div data-win-bind="style.backgroundImage: backgroundUrl;" class="thumbnail"></div></div>

Page 28: Creating windows store java script apps

DATA SOURCE TYPESWinJS.Binding.List - synchronous data source, in-memorydata source (an array), all its data must be available fordisplayStorageDataSource - built-in support for the Windows filesystemVirtualizedDataSource - custom implementation

Page 29: Creating windows store java script apps

UXYou should provide both pointer and touch experience to your

user.

The pointer is an old school, touch control requires some skillsfrom developer.

Page 30: Creating windows store java script apps

OBJECT ROTATIONHilo\Hilo\rotate\TouchProvider.js

function TouchProviderConstructor(inputElement) {

var recognizer = new Windows.UI.Input.GestureRecognizer(); recognizer.gestureSettings = Windows.UI.Input.GestureSettings.manipulationRotate; inputElement.addEventListener("MSPointerDown", function (evt) { var pp = evt.currentPoint; if (pp.pointerDevice.pointerDeviceType === pointerDeviceType.touch) { recognizer.processDownEvent(pp); } }, false);

inputElement.addEventListener("MSPointerMove", function (evt) { var pps = evt.intermediatePoints; if (pps[0] && pps[0].pointerDevice.pointerDeviceType === pointerDeviceType.touch) { recognizer.processMoveEvents(pps); } }, false);

inputElement.addEventListener("MSPointerUp", function (evt) { var pp = evt.currentPoint; if (pp.pointerDevice.pointerDeviceType === pointerDeviceType.touch) { recognizer.processUpEvent(pp); } }, false);

Page 31: Creating windows store java script apps

PERFORMANCE TIPSLimit the start timeEmphasize responsivenessUse thumbnails for quick renderingRetrieve thumbnails when accessing itemsRelease media and stream resources when they're nolonger neededOptimize ListView performanceKeep DOM interactions to a minimumOptimize property accessUse independent animationsManage layout efficientlyStore state efficientlyKeep your app’s memory usage low when it's suspendedMinimize the amount of resources that your app uses

Page 32: Creating windows store java script apps

IN DEPTH is a technique in which the system creates

bytecode for each JavaScript file once, rather than re-creatingthe bytecode each time it starts the app (30% improvment in

large apps).

Avoid synchronous API calls. Break proccessing operationsinto series of smaller operations.

Bytecode caching

Page 33: Creating windows store java script apps

IN DEPTHUse Windows Runtime thumbnail APIs to cache and show

thumbnails.

Use DOM objects only to store information that directlyaffects how the DOM lays out or draws elements.

Certain types of animations are offloaded from the UI threadto GPU-accelerated system thread.

Page 34: Creating windows store java script apps

IN DEPTHCombine API calls to reduce the number of layout passes.

Store session data in the sessionState in-memory object.

When your app begins the suspension process, it should freeany large objects that can be easily rebuilt when it resumes.

Page 35: Creating windows store java script apps

WAYS TO TEST YOUR APPUnit testingIntegration testingUX testingSecurity testingLocalization testingAccessibility testingPerformance testingDevice testing

Page 36: Creating windows store java script apps

MOCHAHilo.Specifications\specs\queries\imageQueryBuilder.spec.js

describe("Image Query Builder", function () {

var queryBuilder, storageFolder;

beforeEach(function (done) { queryBuilder = new Hilo.ImageQueryBuilder();

var whenFolder = Windows.Storage.ApplicationData.current.localFolder.getFolderAsync("Indexed"); whenFolder.then(function (folder) { storageFolder = folder; done(); }); });

Page 37: Creating windows store java script apps

ASYNC TESTSHilo.Specifications\specs\queries\imageQueryBuilder.spec.js

describe("when executing a query that specifies the number of images to load", function () { var queryResult;

beforeEach(function () { queryResult = queryBuilder .count(1) .build(storageFolder) .execute(); });

it("should load the specified number of images", function (done) { queryResult.then(function (images) { expect(images.length).equals(1); done(); }).done(null, done); });});

Page 38: Creating windows store java script apps

HANDLING EXCEPTIONSMocha test runner should intercepts this exception and

reports it as a test failure.

But WinJS is intercepting all promises errors.

.done function is never called, test will go into a wait state andMocha will time out (2 seconds), omitting the fail message.

Page 39: Creating windows store java script apps

TEST APP STATES

Debug Location toolbar

Page 41: Creating windows store java script apps

DETAILS

source -

app docs -

http://bit.ly/win8js

http://bit.ly/pphilojs

[email protected]

@2j2e