Java script data binding mit jQuery Mobile

Post on 13-May-2015

1.557 views 4 download

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.

Transcript of Java script data binding mit jQuery Mobile

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