Upload
stefan-scheidt
View
1.557
Download
4
Embed Size (px)
DESCRIPTION
JavaScript-Clients sind ein wichtiger Bestandteil des Mobile Computings, die wart- und testbare Entwicklung ist aber eine Herausforderung. Data Binding erleichtet das Unterfangen durch die klare Trennung von Anwendungscode und UI und vereinfacht den Code dadurch deutlich. Der Vortrag erläutert das Konzept und verdeutlicht die Vorteile an einem Beispiel unter Einsatz von AngularJS und jQuery Mobile.
Citation preview
JavaScript Data Binding mit jQuery Mobile
Tobias Bosch & Stefan Scheidt / OPITZ CONSULTING GmbH
Wer sind wir?
tobias.bosch@opitz-‐consulQng.com (@Qgbro)
stefan.scheidt@opitz-‐consulQng.com
(@beezlebug)
© OPITZ CONSULTING GmbH 2011 Seite 3
Das Bild kann nicht angezeigt werden. Dieser Computer verfügt möglicherweise über zu wenig Arbeitsspeicher, um das Bild zu öffnen, oder das
OPITZ CONSULTING Vorlage Powerpoint 2011; Version 1.3; 10.05.2011; TGA, KSH
1Pager • Layout ausschließlich für den
1Pager • Einsatz ist bei Konferenzen,
ext. Veranstaltungen etc. obligatorisch. Die Folie ist Folie 2 (nach der Titelfolie)
• Der Inhalt darf nicht verändert werden.
• Ausnahme: Der Block Märkte darf situativ um Partnerlogos (ORACLE, etc.) ergänzt werden
© OPITZ CONSULTING GmbH 2011
Märkte n Java n SOA n ORACLE n BI/DWH n Outtasking
Kunden n Branchen-
übergreifend n Über 600
Kunden
Leistungs- angebot n IT-Strategie n Beratung n Implementierung n Betrieb n Training
Fakten n Gründung 1990 n 400 Mitarbeiter n 8 Standorte in D/
PL
Industrie / Versorger / Telekommunikation
29%
Handel / Logistik / Dienstleistungen 29%
42% Öffentliche Auftraggeber /
Banken & Versicherungen / Vereine & Verbände
Wer sind Sie?
In diesem Vortrag geht‘s um...
...die Entwicklung testbarer und wartbarer mobiler Web-‐Apps
Unser Beispiel
Unser Beispiel...
Mobile Web-‐Apps
Architektur
"MulQ Page Web App"
Design: • Das Farbschema ist im Design als „OC 2009“ hinterlegt.
• Ebenso sind die Schri\arten als „OC 2009“ hinterlegt.
• Die Standardfarben sind:
Browser Server
Controller Backend
HTML-‐Page
UI Values Data
"AJAX Web App"
Design: • Das Farbschema ist im Design als „OC 2009“ hinterlegt.
• Ebenso sind die Schri\arten als „OC 2009“ hinterlegt.
• Die Standardfarben sind:
Browser
AJAX-‐ Engine
Server
Controller Change
Events Data Backend
"Single Page Web App"
Design: • Das Farbschema ist im Design als „OC 2009“ hinterlegt.
• Ebenso sind die Schri\arten als „OC 2009“ hinterlegt.
• Die Standardfarben sind:
Browser Server
Controller Backend Data
Bibliotheken
jQuery Mobile
h=p://jquerymobile.com/
Noch einmal unser Beispiel...
<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
<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
<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
DOM-‐TransformaQon durch jQuery Mobile
<input type="checkbox" id="todo1"/> <label for="todo1">create a mobile todo app</label> <div class="ui-‐checkbox"> <input type="checkbox" name="todo.done" id="todo1"> <label class="ui-‐btn ui-‐btn-‐up-‐c ui-‐btn-‐icon-‐left ui-‐btn-‐corner-‐all ui-‐checkbox-‐off" for="todo1" data-‐theme="c"> <span class="ui-‐btn-‐inner ui-‐btn-‐corner-‐all"> <span class="ui-‐btn-‐text">create a mobile todo app</span> <span class="ui-‐icon ui-‐icon-‐checkbox-‐off ui-‐icon-‐shadow"></span> </span> </label> </div>
jQuery Mobile Markup TransformaIon
Vorher
<input type="checkbox" id="todo1"/> <label for="todo1">create a mobile todo app</label> <div class="ui-‐checkbox"> <input type="checkbox" name="todo.done" id="todo1"> <label class="ui-‐btn ui-‐btn-‐up-‐c ui-‐btn-‐icon-‐left ui-‐btn-‐corner-‐all ui-‐checkbox-‐off" for="todo1" data-‐theme="c"> <span class="ui-‐btn-‐inner ui-‐btn-‐corner-‐all"> <span class="ui-‐btn-‐text">create a mobile todo app</span> <span class="ui-‐icon ui-‐icon-‐checkbox-‐off ui-‐icon-‐shadow"></span> </span> </label> </div>
jQuery Mobile Markup TransformaIon
Nachher
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'); $('#input').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'); $('#input').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'); $('#input').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'); $('#input').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'); $('#input').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'); $('#input').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'); $('#input').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'); $('#input').val(''); } function entryTemplate(index, name) { var id = 'todo' + index; return '<input type="checkbox" id="' + id + '"/>' + '<label for="' + id + '">' + name + '</label>'; }
function TodoController() { this.todos = []; this.inputText = ''; } TodoController.prototype = { addTodo: function() { this.todos.push({
name: this.inputText, done: false });
this.inputText = ''; } }
Das Ziel ist aber:
MVC with Dependency InjecQon
Angular JS
h=p://angularjs.org/#/
Two-‐Way Data Binding
DeclaraQve UI Templates
Framework
Two-‐Way Databinding
Data-‐binding
Controller DOM
read write
watch
read write
watch
Scopes
Scope
Object
$watch(<expr>, <callback>)
$set(<expr>, <value>)
$get(<expr>) Expressions
'inputText'
'todos.length'
...
<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 = ''; } TodoController.prototype = { addTodo: function() { this.todos.push({
name: this.inputText, done: false });
this.inputText = ''; } }
Der Controller
inputText: 'new todo' todos: [...]
TodoController-‐Scope
todo: { done: false name: 'makemoney' }
Repeater Scope todo: { done: false name: 'makemoney' }
Repeater Scope todo: { done: false name: 'makemoney' }
Repeater Scope
<div data-‐role="page" ng:controller="TodoController">
<input type="text" name="inputText"
<div ng:repeat="todo in todos">
<input type="checkbox" name="todo.done"/>
<label> {{todo.name}} </label>
erzeugt
bindet
bindet
erzeugt
bindet
bindet
Damit ist das Ziel fast erreicht...
Die DOM-‐ManipulaQonen von
jQuery Mobile und Angular JS
müssen "nur noch" koordiniert werden.
Dazu später mehr!
MVC with Dependency InjecQon
Angular JS
h=p://angularjs.org/#/
Two-‐Way Data Binding
DeclaraQve UI Templates
Framework
var readUrl = 'https://secure.openkeyval.org/'; var jsonp = ...; var waitdialog = ...; function read(key, success) { var url = readUrl + key; waitdialog.show(); jsonp(url, function(data) { success(data); waitdialog.hide(); }); }
Backend-‐Anbindung
var readUrl = 'https://secure.openkeyval.org/'; var jsonp = ...; var waitdialog = ...; function read(key, success) { var url = readUrl + key; waitdialog.show(); jsonp(url, function(data) { success(data); waitdialog.hide(); }); }
Backend-‐Anbindung
var readUrl = 'https://secure.openkeyval.org/'; var jsonp = ...; var waitdialog = ...; function read(key, success) { var url = readUrl + key; waitdialog.show(); jsonp(url, function(data) { success(data); waitdialog.hide(); }); }
Backend-‐Anbindung
angular.service('jsonp', jsonpFactory); angular.service('waitdialog', waitdialogFactory); function todoStoreFactory(jsonp, waitdialog) { function read(...) { ... } function write(...) { ... } return { read: read, write: write } } todoStoreFactory.$inject = ['jsonp', 'waitdialog']; angular.service('todostore', todoStoreFactory);
Services und DI mit Angular
angular.service('jsonp', jsonpFactory); angular.service('waitdialog', waitdialogFactory); function todoStoreFactory(jsonp, waitdialog) { function read(...) { ... } function write(...) { ... } return { read: read, write: write } } todoStoreFactory.$inject = ['jsonp', 'waitdialog']; angular.service('todostore', todoStoreFactory);
Services und DI mit Angular
angular.service('jsonp', jsonpFactory); angular.service('waitdialog', waitdialogFactory); function todoStoreFactory(jsonp, waitdialog) { function read(...) { ... } function write(...) { ... } return { read: read, write: write } } todoStoreFactory.$inject = ['jsonp', 'waitdialog']; angular.service('todostore', todoStoreFactory);
Services und DI mit Angular
angular.service('jsonp', jsonpFactory); angular.service('waitdialog', waitdialogFactory); function todoStoreFactory(jsonp, waitdialog) { function read(...) { ... } function write(...) { ... } return { read: read, write: write } } todoStoreFactory.$inject = ['jsonp', 'waitdialog']; angular.service('todostore', todoStoreFactory);
Services und DI mit Angular
function TodoController(todoStore) { ... } TodoController.$inject = ['todoStore'];
Di für Controller
function TodoController(todoStore) { ... } TodoController.$inject = ['todoStore'];
Di für Controller
function TodoController(todoStore) { ... } TodoController.$inject = ['todoStore'];
Di für Controller
One more thing...
IntegraQon von AngularJS und jQuery Mobile
jquery-‐mobile-‐angular-‐adapter
KoordinaQon von jQuery Mobile und Angular JS
Erweiterungen für mobile Web-‐Apps
Open Source unter heps://github.com/Qgbro/
jquery-‐mobile-‐angular-‐adapter
Paging in Listen
<div ng:repeat= "todo in todos.$paged()">
... </div> <div ng:if= "todos.$paged().hasMorePages()"> <a href="#" ngm:click= "todos.$paged().loadNextPage()"> Load more </a> </div>
Paging in Listen
<div ng:repeat= "todo in todos.$paged()">
... </div> <div ng:if= "todos.$paged().hasMorePages()"> <a href="#" ngm:click= "todos.$paged().loadNextPage()"> Load more </a> </div>
Paging in Listen
<div ng:repeat= "todo in todos.$paged()">
... </div> <div ng:if= "todos.$paged().hasMorePages()"> <a href="#" ngm:click= "todos.$paged().loadNextPage()"> Load more </a> </div>
Mobile Events
<div id="main" data-‐role="page" ng:event="swipeleft:showSettings()"> ... </div> <div id="settings" data-‐role="page" ng:event="swiperight:back()"> ... </div>
NavigaQon über Pages
function TodoController(todoStore, activePage) { ... } TodoController.prototype = { onActivate: function(prevscope) { if (prevscope && prevscope.storageKey) { this.storageKey = prevscope.storageKey; this.refreshTodos(); } }, showSettings: function() { this.activePage("#settings"); } };
NavigaQon über Pages
function TodoController(todoStore, activePage) { ... } TodoController.prototype = { onActivate: function(prevscope) { if (prevscope && prevscope.storageKey) { this.storageKey = prevscope.storageKey; this.refreshTodos(); } }, showSettings: function() { this.activePage("#settings"); } };
NavigaQon über Pages
function TodoController(todoStore, activePage) { ... } TodoController.prototype = { onActivate: function(prevscope) { if (prevscope && prevscope.storageKey) { this.storageKey = prevscope.storageKey; this.refreshTodos(); } }, showSettings: function() { this.activePage("#settings"); } };
Wait-‐Dialog Service
waitDialog.show('loading'); waitDialog.hide();
waitDialog.show('click to abort', onClickCallback);
Fazit
Auch bei der Entwicklung von JavaScript Clients
sollten geeignete Entwurfsmuster angewendet werden!
Fazit
Bibliotheken und Frameworks helfen bei der Umsetzung!
Fazit
Eine praxiserprobte KombinaQon:
jQuery Mobile + AngularJS + Adapter
In the hive 11: nectar and pollen by Max xx, hep://www.flickr.com/photos/max_westby/4567762490
Books
By Rodrigo Galindez, hep://www.flickr.com/photos/rodrigogalindez/4637637337/
Vielen Dank für Ihr Interesse!
@Qgbro
@beezlebug