JavaScript Data Binding mit jQuery Mobile - OPITZ CONSULTING - Tobias Bosch - Stefan Scheidt

3,764 views
3,695 views

Published on

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.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,764
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

JavaScript Data Binding mit jQuery Mobile - OPITZ CONSULTING - Tobias Bosch - Stefan Scheidt

  1. 1. Tobias Bosch &<br />Stefan Scheidt/ OPITZ CONSULTING GmbH<br />JavaScript Data Binding<br />mitjQuery Mobile<br />
  2. 2. Wer sind wir?<br />tobias.bosch@opitz-consulting.com<br />(@tigbro)<br />stefan.scheidt@opitz-consulting.com<br />(@beezlebug)<br />
  3. 3.
  4. 4. Wer sind Sie?<br />
  5. 5. In diesem Vortrag geht‘s um...<br />...die Entwicklung testbarer und wartbarer mobiler Web-Apps<br />
  6. 6. Unser Beispiel<br />
  7. 7. Mobile Web-Apps<br />
  8. 8. Architektur<br />
  9. 9. "Multi Page Web App"<br />Browser<br />Server<br />HTML-Page<br />Controller<br />Backend<br />Data<br />Design:<br /><ul><li>Das Farbschema ist im Design als „OC 2009“ hinterlegt.
  10. 10. Ebenso sind die Schriftarten als „OC 2009“ hinterlegt.
  11. 11. Die Standardfarben sind:</li></ul>UI Values<br />
  12. 12. "AJAX Web App"<br />Browser<br />Server<br />Controller<br />Backend<br />AJAX-<br />Engine<br />Change<br />Data<br />Design:<br /><ul><li>Das Farbschema ist im Design als „OC 2009“ hinterlegt.
  13. 13. Ebenso sind die Schriftarten als „OC 2009“ hinterlegt.
  14. 14. Die Standardfarben sind:</li></ul>Events<br />
  15. 15. "Single Page Web App"<br />Browser<br />Server<br />Controller<br />Backend<br />Data<br />Design:<br /><ul><li>Das Farbschema ist im Design als „OC 2009“ hinterlegt.
  16. 16. Ebenso sind die Schriftarten als „OC 2009“ hinterlegt.
  17. 17. Die Standardfarben sind:</li></li></ul><li>Bibliotheken<br />
  18. 18. jQuery Mobile<br />http://jquerymobile.com/<br />
  19. 19. Noch einmal unser Beispiel...<br />
  20. 20. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />jQuery Mobile Markup<br />
  21. 21. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />jQuery Mobile Markup<br />
  22. 22. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />jQuery Mobile Markup<br />
  23. 23. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />jQuery Mobile Markup<br />
  24. 24. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />jQuery Mobile Markup<br />
  25. 25. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />jQuery Mobile Markup<br />
  26. 26. DOM-Transformation<br />durch jQuery Mobile<br />
  27. 27. Vorher<br /><input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /><div class="ui-checkbox"><br /> <input type="checkbox" name="todo.done" id="todo1"><br /> <labelclass="ui-btnui-btn-up-c ui-btn-icon-left<br />ui-btn-corner-all ui-checkbox-off"<br />for="todo1" data-theme="c"><br /> <span class="ui-btn-innerui-btn-corner-all"><br /> <span class="ui-btn-text">createa mobile todoapp</span><br /><span class="ui-icon ui-icon-checkbox-off<br />ui-icon-shadow"></span><br /></span><br /></label><br /></div><br />jQuery Mobile Markup Transformation<br />
  28. 28. <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /><div class="ui-checkbox"><br /> <input type="checkbox" name="todo.done" id="todo1"><br /> <labelclass="ui-btnui-btn-up-c ui-btn-icon-left<br />ui-btn-corner-all ui-checkbox-off"<br />for="todo1" data-theme="c"><br /> <span class="ui-btn-innerui-btn-corner-all"><br /> <span class="ui-btn-text">createa mobile todoapp</span><br /><span class="ui-icon ui-icon-checkbox-off<br />ui-icon-shadow"></span><br /></span><br /></label><br /></div><br />Nachher<br />jQuery Mobile Markup Transformation<br />
  29. 29. Manuelles Binding<br />
  30. 30. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  31. 31. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  32. 32. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  33. 33. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  34. 34. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  35. 35. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  36. 36. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  37. 37. $('#addTodo').submit(function(event) {<br />addTodo();<br />event.preventDefault();<br />});<br />functionaddTodo() {<br />varinputText = $('#inputText').val();<br />varlist = $('#todos');<br />varentryCount = list.find('input').length;<br />list.append(entryTemplate(entryCount, inputText));<br />list.trigger('create');<br /> $('#input').val('');<br />}<br />functionentryTemplate(index, name) {<br />varid = 'todo' + index;<br />return '<input type="checkbox" id="' + id + '"/>' +<br /> '<labelfor="' + id + '">' + name + '</label>';<br />}<br />
  38. 38. functionTodoController() {<br />this.todos= [];<br />this.inputText = '';<br />}<br />TodoController.prototype = {<br />addTodo: function() {<br />this.todos.push({<br />name: this.inputText, <br />done: false<br /> });<br />this.inputText= '';<br />}<br />}<br />Das Ziel ist aber:<br />
  39. 39. Angular JS<br />MVC with<br />DependencyInjection<br />Declarative<br />UI Templates<br />Two-Way<br />Data Binding<br />Framework<br />http://angularjs.org/#/<br />
  40. 40. Two-Way Databinding<br />read<br />write<br />Controller<br />read<br />write<br />DOM<br />Data-binding<br />watch<br />watch<br />
  41. 41. Scopes<br />Scope<br />$get(<expr>)<br />Object<br />$set(<expr>, <value>)<br />$watch(<expr>, <callback>)<br />
  42. 42. <div id="main" data-role="page"><br /><div data-role="header"><br /><h1>Todos</h1><br /><a href="">Save</a><br /><a href="#settings">Settings</a><br /></div><br /><div data-role="content"><br /> <div data-role="fieldcontain"><br /> <form data-ajax="false"><br /> <input type="text"><br /> </form><br /></div><br /><fieldsetdata-role="controlgroup"><br /> <input type="checkbox" id="todo1"/><br /><labelfor="todo1">create a mobile todoapp</label><br /> </fieldset><br /></div><br /></div><br />Das DOM<br />
  43. 43. functionTodoController() {<br />this.todos= [];<br />this.inputText = '';<br />}<br />TodoController.prototype = {<br />addTodo: function() {<br />this.todos.push({<br />name: this.inputText, <br />done: false<br /> });<br />this.inputText= '';<br />}<br />}<br />Der Controller<br />
  44. 44. inputText: 'newtodo'<br />todos: [...]<br />TodoController-Scope<br /><div data-role="page"<br />ng:controller="TodoController"><br />erzeugt<br /><input type="text" <br />name="inputText"<br />bindet<br />bindet<br /><div ng:repeat="todo in todos"><br />erzeugt<br />Repeater Scope<br />Repeater Scope<br />Repeater Scope<br />todo: {<br />done: false<br />name: 'makemoney'<br />}<br />todo: {<br />done: false<br />name: 'makemoney'<br />}<br />todo: {<br />done: false<br />name: 'makemoney'<br />}<br /><input type="checkbox"<br />name="todo.done"/><br />bindet<br /><label><br />{{todo.name}}<br /></label><br />bindet<br />
  45. 45. Damit ist das Ziel fast erreicht...<br />
  46. 46. Die DOM-Manipulationen von<br />jQuery Mobile und Angular JS<br />müssen "nur noch" koordiniert werden.<br />
  47. 47. Dazu später mehr!<br />
  48. 48. MVC with<br />DependencyInjection<br />Angular JS<br />Declarative<br />UI Templates<br />Two-Way<br />Data Binding<br />Framework<br />http://angularjs.org/#/<br />
  49. 49. var readUrl = 'https://secure.openkeyval.org/';<br />var jsonp = ...;<br />var waitdialog = ...;<br />functionread(key, success) {<br /> var url = readUrl + key;<br />waitdialog.show();<br />jsonp(url, function(data) {<br />success(data);<br />waitdialog.hide();<br /> });<br />}<br />Backend-Anbindung<br />
  50. 50. var readUrl = 'https://secure.openkeyval.org/';<br />var jsonp = ...;<br />var waitdialog = ...;<br />functionread(key, success) {<br /> var url = readUrl + key;<br />waitdialog.show();<br />jsonp(url, function(data) {<br />success(data);<br />waitdialog.hide();<br /> });<br />}<br />Backend-Anbindung<br />
  51. 51. var readUrl = 'https://secure.openkeyval.org/';<br />var jsonp = ...;<br />var waitdialog = ...;<br />functionread(key, success) {<br /> var url = readUrl + key;<br />waitdialog.show();<br />jsonp(url, function(data) {<br />success(data);<br />waitdialog.hide();<br /> });<br />}<br />Backend-Anbindung<br />
  52. 52. angular.service('jsonp', jsonpFactory);<br />angular.service('waitdialog', waitdialogFactory);<br />functiontodoStoreFactory(jsonp, waitdialog) {<br />functionread(...) { ... }<br />functionwrite(...) { ... }<br /> return {<br />read: read,<br />write: write<br /> }<br />}<br />todoStoreFactory.$inject = ['jsonp', 'waitdialog'];<br />angular.service('todostore', todoStoreFactory);<br />Services und DI mit Angular<br />
  53. 53. angular.service('jsonp', jsonpFactory);<br />angular.service('waitdialog', waitdialogFactory);<br />functiontodoStoreFactory(jsonp, waitdialog) {<br />functionread(...) { ... }<br />functionwrite(...) { ... }<br /> return {<br />read: read,<br />write: write<br /> }<br />}<br />todoStoreFactory.$inject = ['jsonp', 'waitdialog'];<br />angular.service('todostore', todoStoreFactory);<br />Services und DI mit Angular<br />
  54. 54. angular.service('jsonp', jsonpFactory);<br />angular.service('waitdialog', waitdialogFactory);<br />functiontodoStoreFactory(jsonp, waitdialog) {<br />functionread(...) { ... }<br />functionwrite(...) { ... }<br /> return {<br />read: read,<br />write: write<br /> }<br />}<br />todoStoreFactory.$inject = ['jsonp', 'waitdialog'];<br />angular.service('todostore', todoStoreFactory);<br />Services und DI mit Angular<br />
  55. 55. angular.service('jsonp', jsonpFactory);<br />angular.service('waitdialog', waitdialogFactory);<br />functiontodoStoreFactory(jsonp, waitdialog) {<br />functionread(...) { ... }<br />functionwrite(...) { ... }<br /> return {<br />read: read,<br />write: write<br /> }<br />}<br />todoStoreFactory.$inject = ['jsonp', 'waitdialog'];<br />angular.service('todostore', todoStoreFactory);<br />Services und DI mit Angular<br />
  56. 56. functionTodoController(todoStore) {<br /> ...<br />}<br />TodoController.$inject = ['todoStore'];<br />Di für Controller<br />
  57. 57. functionTodoController(todoStore) {<br /> ...<br />}<br />TodoController.$inject = ['todoStore'];<br />Di für Controller<br />
  58. 58. functionTodoController(todoStore) {<br /> ...<br />}<br />TodoController.$inject = ['todoStore'];<br />Di für Controller<br />
  59. 59. Onemorething...<br />Integration von AngularJS<br />und jQuery Mobile<br />
  60. 60. jquery-mobile-angular-adapter<br />Koordination von jQuery Mobile und Angular JS<br />Erweiterungen für mobile Web-Apps<br />Open Source unter <br />https://github.com/tigbro/<br />jquery-mobile-angular-adapter<br />
  61. 61. Paging in Listen<br /><div ng:repeat=<br /> "todo in todos.$paged()"><br />...<br /></div><br /><div ng:if=<br /> "todos.$paged().hasMorePages()"><br /><a href="#" ngm:click=<br /> "todos.$paged().loadNextPage()"><br /> Load more<br /> </a><br /></div><br />
  62. 62. Paging in Listen<br /><div ng:repeat=<br /> "todo in todos.$paged()"><br />...<br /></div><br /><div ng:if=<br /> "todos.$paged().hasMorePages()"><br /><a href="#" ngm:click=<br /> "todos.$paged().loadNextPage()"><br /> Load more<br /> </a><br /></div><br />
  63. 63. Paging in Listen<br /><div ng:repeat=<br /> "todo in todos.$paged()"><br />...<br /></div><br /><div ng:if=<br /> "todos.$paged().hasMorePages()"><br /><a href="#" ngm:click=<br /> "todos.$paged().loadNextPage()"><br /> Load more<br /> </a><br /></div><br />
  64. 64. Mobile Events<br /><div id="main" data-role="page" <br />ng:event="swipeleft:showSettings()"><br /> ...<br /></div><br /><div id="settings" data-role="page" <br />ng:event="swiperight:back()"><br /> ...<br /></div><br />
  65. 65. Navigation über Pages<br />functionTodoController(todoStore, activePage) { ... }<br />TodoController.prototype= {<br />onActivate: function(prevscope) {<br />if (prevscope && prevscope.storageKey) {<br />this.storageKey = prevscope.storageKey;<br />this.refreshTodos();<br /> }<br />},<br />showSettings: function() {<br />this.activePage("#settings");<br />}<br />};<br />
  66. 66. Navigation über Pages<br />functionTodoController(todoStore, activePage) { ... }<br />TodoController.prototype= {<br />onActivate: function(prevscope) {<br />if (prevscope && prevscope.storageKey) {<br />this.storageKey = prevscope.storageKey;<br />this.refreshTodos();<br /> }<br />},<br />showSettings: function() {<br />this.activePage("#settings");<br />}<br />};<br />
  67. 67. Navigation über Pages<br />functionTodoController(todoStore, activePage){ ... }<br />TodoController.prototype= {<br />onActivate: function(prevscope) {<br />if (prevscope && prevscope.storageKey) {<br />this.storageKey = prevscope.storageKey;<br />this.refreshTodos();<br /> }<br />},<br />showSettings: function() {<br />this.activePage("#settings");<br />}<br />};<br />
  68. 68. Wait-Dialog Service<br />waitDialog.show('loading');<br />waitDialog.hide();<br />waitDialog.show('clicktoabort', <br />onClickCallback);<br />
  69. 69. Fazit<br />Auch bei der Entwicklung <br />von JavaScript Clients<br />sollten geeignete Entwurfsmuster<br />angewendet werden!<br />
  70. 70. Fazit<br />Bibliotheken und Frameworks<br />helfen bei der Umsetzung!<br />
  71. 71. Fazit<br />Eine praxiserprobte Kombination:<br />jQuery Mobile<br />+ AngularJS<br />+ Adapter<br />
  72. 72. In thehive 11: nectarandpollen<br />byMax xx, http://www.flickr.com/photos/max_westby/4567762490<br />BooksBy Rodrigo Galindez, http://www.flickr.com/photos/rodrigogalindez/4637637337/<br />
  73. 73. Vielen Dankfür Ihr Interesse!@tigbro@beezlebug<br />

×