var inputText

Transcripción

var inputText
Bastian Schramm, Stefan Scheidt, Tobias Bosch | Opitz Consulting GmbH
Mobile JavaScript-Web-App professionell entwickeln
Entwurfsmuster für JavaScript Web-Apps:
Data Binding
Teil 2
Mobile JavaScript-­‐Web-­‐Apps Entwurfsmuster für JavaScript Web-­‐Apps: Data Binding In diesem Vortrag geht‘s um... ...die Entwicklung testbarer und wartbarer JavaScript Web-­‐Apps ...durch Data Binding Unser Beispiel Architektur "Single Page Web App" n
a
m
t r
e
i
r
u
t
?
k
?
u
r
?
t
e
d
Wie s lient-­‐CoBrowser den C
Model View Controller Server Data Backend Two Way Data Binding Markup mit jQuery Mobile h=p://jquerymobile.com/ <div id="main" data-­‐role="page"> <div data-­‐role="header"> <h1>Todos</h1> <a href="">Save</a> <a href="#settings">Settings</a> </div> <div data-­‐role="content"> <div data-­‐role="fieldcontain"> <form data-­‐ajax="false"> <input type="text"> </form> </div> <fieldset data-­‐role="controlgroup"> <input type="checkbox" id="todo1"/> <label for="todo1">create a mobile todo app</label> </fieldset> </div> </div> jQuery Mobile Markup <div id="main" data-­‐role="page"> <div data-­‐role="header"> <h1>Todos</h1> <a href="">Save</a> <a href="#settings">Settings</a> </div> <div data-­‐role="content"> <div data-­‐role="fieldcontain"> <form data-­‐ajax="false"> <input type="text"> </form> </div> <fieldset data-­‐role="controlgroup"> <input type="checkbox" id="todo1"/> <label for="todo1">create a mobile todo app</label> </fieldset> </div> </div> jQuery Mobile Markup Manuelles Binding $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } $('#addTodo').submit(function(event) { addTodo(); event.preventDefault(); }); function addTodo() { var inputText = $('#inputText').val(); var list = $('#todos'); var entryCount = list.find('input').length; list.append(entryTemplate(entryCount, inputText)); list.trigger('create'); $('#inputText').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; } Das Ziel ist aber: function TodoController() { this.todos = []; this.inputText = ''; this.addTodo = function() { this.todos.push({ name: this.inputText, done: false }); this.inputText = ''; }; } Angular JS DeclaraRve UI Templates MVC with Dependency InjecRon Two-­‐Way Data Binding Framework h=p://angularjs.org/#/ Two-­‐Way Databinding DOM read write watch Data-­‐
binding read write watch Controller Scopes Expressions 'inputText' 'todos.length' Scope ... read / write Watch Watch last value Watch last value expression last value expression callback expression callback callback $watch(<expr>, <callback>) $digest() Demo <div id="main" data-­‐role="page"> <div data-­‐role="header"> <h1>Todos</h1> <a href="">Save</a> <a href="#settings">Settings</a> </div> <div data-­‐role="content"> <div data-­‐role="fieldcontain"> <form data-­‐ajax="false"> <input type="text"> </form> </div> <fieldset data-­‐role="controlgroup"> <input type="checkbox" id="todo1"/> <label for="todo1">create a mobile todo app</label> </fieldset> </div> </div> Das DOM function TodoController() { this.todos = []; this.inputText = ''; this.addTodo = function() { this.todos.push({ name: this.inputText, done: false }); this.inputText = ''; }; } Der Controller function TodoController($scope) { $scope.todos = []; $scope.inputText = ''; $scope.addTodo = function() { $scope.todos.push({ name: $scope.inputText, done: false }); $scope.inputText = ''; }; } TodoController.$inject = ['$scope']; Der Controller <div data-­‐role="page" ng-­‐controller="TodoController"> erzeugt <input type="text" ng-­‐model="inputText" bindet TodoController-­‐Scope inputText: 'new todo' todos: [...] bindet <div ng-­‐repeat="todo in todos"> erzeugt <input type="checkbox" ng-­‐model="todo.done"/> bindet <label> {{todo.name}} </label> bindet Repeater cope Repeater SScope Scope Repeater todo: { todo: { todo: { done: false done: false done: false name: 'makemoney' name: 'makemoney' name: 'makemoney' } } } Dependency InjecRon Beispiel: Backend-­‐Anbindung waitDialog todoController key todoStore value key value refreshTodos ... read ... todoStore waitDialog jsonp key value ... ... jsonp key value ... ... Erinnerung: Angular Services var module = angular.module("todo", []); module.factory('jsonp', jsonpFactory); module.factory('waitdialog', waitdialogFactory); function todoStoreFactory(jsonp, waitdialog) { // ... } todoStoreFactory.$inject = ['jsonp', 'waitdialog']; module.factory('todoStore', todoStoreFactory); DI für Controller function TodoController($scope, todoStore) { ... } TodoController.$inject = ['$scope', 'todoStore']; module.controller("rylc.TodoController", TodoController); DI für Controller function TodoController($scope, todoStore) { ... } TodoController.$inject = ['$scope', 'todoStore']; module.controller("rylc.TodoController", TodoController); DI für Controller function TodoController($scope, todoStore) { ... } TodoController.$inject = ['$scope', 'todoStore']; module.controller("rylc.TodoController", TodoController); Erläuterungen zu RYLC Module-­‐PaZern + DI mit Angular Module-­‐PaZern + DI mit Angular (function(angular) { function LoginController($scope, backendService, $navigate) { // ... } LoginController.$inject = ['$scope','backendService', '$navigate']; var module = angular.module("rylc-­‐controllers", ["rylc-­‐services"]); module.controller("rylc.LoginController", LoginController); })(window.angular); Module-­‐PaZern + DI mit Angular (function(angular) { function LoginController($scope, backendService, $navigate) { // ... } LoginController.$inject = ['$scope','backendService', '$navigate']; var module = angular.module("rylc-­‐controllers", ["rylc-­‐services"]); module.controller("rylc.LoginController", LoginController); })(window.angular); Module-­‐PaZern + DI mit Angular (function(angular) { function LoginController($scope, backendService, $navigate) { // ... } LoginController.$inject = ['$scope','backendService', '$navigate']; var module = angular.module("rylc-­‐controllers", ["rylc-­‐services"]); module.controller("rylc.LoginController", LoginController); })(window.angular); Oberflächentests mit uitest.js Oberflächentests describe('login', function() { uit.url('/rylc-­‐html5/index.jsp'); uit.append(function() { mockBackend(); }); ... }); Oberflächentests describe('login', function() { uit.url('/rylc-­‐html5/index.jsp'); uit.append(function() { mockBackend(); }); ... }); Oberflächentests Glob
describe('login', function() { al Fu
ncRo
uit.url('/rylc-­‐html5/index.jsp'); from
n testu
Rls.js
uit.append(function() { mockBackend(); }); ... }); Oberflächentests var someUsername = "someUsername"; var somePassword = "somePassword"; it('should show login page when visiting application', function() { uit.runs(function() { expect(activePageId()).toBe('loginPage'); }); }); it('should allow login when username and password not empty', function() { uit.runs(function() { value("#username", someUsername); value("#password", somePassword); expect(enabled(".login")).toBeTruthy(); }); }); Oberflächentests var someUsername = "someUsername"; var somePassword = "somePassword"; it('should show login page when visiting application', function() { Glob
al Fu
uit.runs(function() { ncRo
from
ns testu
expect(activePageId()).toBe('loginPage'); Rls.js
}); }); it('should allow login when username and password not empty', function() { uit.runs(function() { value("#username", someUsername); value("#password", somePassword); expect(enabled(".login")).toBeTruthy(); }); }); Oberflächentests var someCustomer = { id: 42, name: "someName" }; it('should show the welcome page after successful login', function() { uit.runs(function() { backendServiceResult('login').resolve(someCustomer); value("#username", someUsername); value("#password", somePassword); click(".login"); }); uit.runs(function() { expect(backendService().login) .toHaveBeenCalledWith(someUsername, somePassword); expect(activePageId()).toBe('welcomePage'); }); }); Oberflächentests Glob
var someCustomer = { id: 42, name: "someName" }; al Fun
c
from
testu Rons Rls.js
it('should show the welcome page after successful login', function() { uit.runs(function() { backendServiceResult('login').resolve(someCustomer); value("#username", someUsername); value("#password", somePassword); click(".login"); }); uit.runs(function() { expect(backendService().login) .toHaveBeenCalledWith(someUsername, somePassword); expect(activePageId()).toBe('welcomePage'); }); }); Fazit Auch bei der Entwicklung von JavaScript Clients sollten geeignete Entwurfsmuster angewendet werden! Frameworks helfen bei der Umsetzung! In the hive 11: nectar and pollen by Max xx, hZp://www.flickr.com/photos/max_westby/4567762490 Double Via by amslerPIX, hZp://www.flickr.com/photos/amslerpix/6242266697/ MacBook Pro Keyboard by superstrikertwo, hZp://www.flickr.com/photos/superstrikertwo/4989727256/ Stubborn Last Drop by RogueSun Media, hZp://www.flickr.com/photos/shuZercat7/627798443/ Und jetzt viel Spaß beim Üben! 

Documentos relacionados