Stateful Web Clients (SPA)Marc MüllerPrincipal Consultant, 4tecture [email protected] / www.4tecture.ch@muellermarc / @4tecture
Agenda
AgendaIntroduction 10’Backend Architecture 10’UI Architecture 15’Views and View Models 10’Recap & Questions5’
IntroductionChapter 1/5
Web and Rich Applications
DB
DB
My Web Applicationahttp://www.url.com
My Web Applicationahttp://www.url.com
PagePagePage
Plug-In
requestresponse
Layout & data
requestresponse
data
requestresponse
Plug-in app
AJAX
SilverlightFlashEtc.
Single Page ApplicationWhole application resides in 1 web page In-Page Navigation (# tags) Content (pages) will be shown or hidden on demand
Heavy use of JavaScript MVVM Framework, Navigation Framework Communication (AJAX Calls)
Client-Side stateful representation In-Memory Browser Storage
Connected to RESTful / HTTP-Service backend (JSON) Dataservice for CRUD operations, Metadata
Benefits of Single Page ApplicationsInteroperability Web Standards (HTML / JavaScript) Cross-Device support May be integrated in Hybrid-Application (App Stores)
Fluent-UI Application feels like rich application UI does not load for each request (visually)
MVVM Declarative databinding Stateful client representation Rich-UI programming model
Great user experience
Offline-Support (Browser Storage)
App Store deployable
Runs on any device
Architectural Overview
UI Endpoint
Data Endpoint
View Layer (HTML, CSS)
Main View(Shell)
Partial View (Application
View)Partial View (Application
View)
Application / BL Layer (JavaScript)- Shell: Navigation, Modules, etc- UI Business Logic: View Models,
Models
Data Access Layer (JavaScript)- Agents: REST / HTTP calls- Stateful data representation
My Web Applicationahttp://www.url.com
Page
Navigation / Views
Viewid=«v1» #view1
Viewid=«v2» #view2
1. Register Views Viewname Hashvalue ViewModel
2. Bind ViewModel selector on view KnockoutJS databind
3. Navigate check if can leave activate view hide & show
hide
hide
VM1
VM2
VM3
Frameworks (used in sample)Durandal Application structure: AMD Modules and HTML Views UI Plumbing
Page Navigation / Routing Life-Cycle events
Breeze Data Access to Data Services LINQ-like query language Navigate Object Graphs / avoid data mashing, Metadata Client Caching Change Tracking / Unit of Work
Sample ApplicationShopping ListDemo 1
Backend Architecture
Chapter 2/5
Server ArchitectureStandard ASP.NET MVC 4 Standard Controller to provide
Main View Sub Views (or loaded directly from client) Provide JavaScript files (or loaded directly from client)
WebAPI Controllers to provide data CRUD access for entities Security and Validation Metadata (BreezeJS)
Building a WebAPI Controller (1/2)Standard Layering and Patterns EF as Data Access Repository Pattern / Unit of Work Pattern Shared Entities / DTO Access Control through ASP.NET Stack
Building a WebAPI Controller (2/2)
DB
EF Context
Enti
ties
Repository
UoW
BL Service
WebAPI Controller
Secu
rity
Web API - BreezeCustom extension for WebAPI
Don’t panic! Can be extended
to use custom services / complex architecture
Support for OData (future)
[BreezeController]public class TodosController : ApiController { readonly EFContextProvider<TodosContext> _contextProvider = new EFContextProvider<TodosContext>(); // ~/api/todos/Metadata [HttpGet] public string Metadata() { return _contextProvider.Metadata(); } // ~/api/todos/Todos // ~/api/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt [HttpGet] public IQueryable<TodoItem> Todos() { return _contextProvider.Context.Todos; } // ~/api/todos/SaveChanges [HttpPost] public SaveResult SaveChanges(JObject saveBundle) { return _contextProvider.SaveChanges(saveBundle); }}
Web API - BreezeSample endpoint for our UI -> get shopping lists[HttpGet]public IQueryable<Common.Entities.ShoppingList> ShoppingLists(){ try { return this.ShoppingListService.GetUserShoppingLists(); } catch (SecurityException) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized)); } catch (Exception) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.InternalServerError)); }}
Backend ServicesDemo 2
Web Application StructureOrganize JavaScript files Separate App-Code and Library Code Don’t pollute the namespace use JavaScript AMD patterns
One big HTML is not maintainable Separate the different views in single files Compose views with sub-views
Maintainable JavaScript Use generators Use TypeScript (demo application)
JavaScript ModularizationAMD – Asynchonous Module Definition Encapsulate code in object Load module on demand / dependency analysis
RequireJS Does dependency resolving Loads the required JavaScript files
define(["require", "exports"], function(require, exports) {
var Group = (function () { function Group() { this.id = ko.observable(0); this.name = ko.observable("");
} return Group; })(); exports.Group = Group; })
Anonymous module definitionDependencies
Web Project StructureDemo 3
UI ArchitectureChapter 3/5
Application Build-upStartup Logic and Configuration -> main.js
Main UI & Navigation -> Shell Router extensions for in-page navigation Map View to ViewModel
In-Memory entity representation -> DataContext
CRUD on WebAPI -> DataManager
Lifecycle EventsDurandal supports many hook-points
Lifecycle Events getView Enables the new object to return a custom view. canDeactivate Allows the previous object to cancel deactivation. canActivate Allows the new object to cancel activation. deactivate Allows the previous object to execute custom deactivation logic. activate Allows the new object to execute custom activation logic. beforeBind Notifies the new object immediately before databinding occurs. afterBind Notifies the new object immediately after databinding occurs. viewAttached Notifies the new object when its view is attached to its parent DOM node.
load data from context
prevent navigation on unsaved changes
setup UI events
UI ArchitectureDemo 4
MVVMChapter 4/5
MVVM - DefinitionModel The actual data representation Provides change-tracking
View Model Contains the view-logic Loads the data Gets event notifications
View The visual representation Uses declarative databinding notified by events (change, click, etc.)
Personfirstname : stringlastname : string
PersonVMpersons: Person[]getPersons()savePerson()
PersonView<div data-bind="text: fullname" />
databinding Events, Commands
KnockoutJSKey Concepts Declarative Bindings Automatic UI Refresh Dependency Tracking Templating
image source: knockoutjs.com
KnockoutJS extensionsKoLite - Lightweight Toolkit for KnockoutJS asyncCommand activity dirtyFlagself.saveCommand = ko.asyncCommand({
execute: function(callback) { $.ajax({ complete: callback,
data: { name: self.name() }, type: 'POST', url: '/save/', success: function(result) { alert('Name saved:' + result) } }) },
canExecute: function(isExecuting) { return !isExecuting && self.name() } })
Databinding with KnockoutJSDemo 5
Recap & QuestionsChapter 5/5
Summary Complete application in Single Page Heavy use of JavaScript Declarative Binding Backend Communication with RESTful /
HTTP-Services (JSON)
Fluid, Interoperable Client Application
Where to start http://learn.knockoutjs.com/ http://learn.breezejs.com/ http://durandaljs.com http://
www.asp.net/single-page-application/overview/templates/hottowel-template
http://www.4tecture.ch/blog
© 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a
commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.