Dojo 1.8 und AMD

Ein Überblick mit Demos

Karsten Lehmann, Mindoo GmbH / @klehmann79

Über uns

  • IBM Business Partner und Notes/Domino Design Partner
  • Eclipse/Expeditor-Plugins und Rich-Client-Anwendungen
  • XPages-Anwendungen
  • Web-Applikationsentwicklung für IBM Websphere und Oracle Glassfish
  • Karsten Lehmann und Tammo Riedinger
  • Seit 2004 Entwickler von MindPlan für die Haus Weilgut GmbH, Mindmapping und Projekt-Management für IBM Notes
  • http://www.mindoo.de

Ziele

  • Konzepte hinter Dojo 1.8 verstehen
  • Nachschlagewerk für neue APIs
  • Ideen und Stichworte für eigene Experimente
  • Tips zur Verwendung von Dojo in XPages in IBM Notes/Domino R9

Was ist Dojo?

  • Umfangreiches, kostenlos nutzbares Web-Toolkit
  • APIs für Web und Mobile nutzbar
  • ein Toolkit: keine klare Trennung in Web und Mobile
  • Dojo 1.8 ist Bestandteil von IBM Notes/Domino R9!

Was ist Dojo?

Ein SDK mit vier Bereichen:

  • dojo - allgemeine Werkzeuge für JS-Entwicklung und Ajax
  • dijit - UI-Komponenten, z.B. Layout und Formularelemente
  • dojox - weitere UI-Komponenten, u.a. Mobile UI
  • util - Werkzeuge zur Build-Generierung und Unittests

Wo finde ich Hilfe?

  • Dojo-API-Dokumentation (Link)
    umfangreiche Sammlung an Tutorials, Referenz- und API-Dokumentation
  • Sitepen-Blog (Link)
  • letzte Lösung:
    Dojo-Quelltext - es ist alles Open Source!

Dojo Releases

  • Juli 2010 - Dojo 1.5
  • März 2011 - Dojo 1.6
  • Oktober 2011 - Lotus Notes 8.5.3 mit Dojo 1.6
  • Dezember 2011 - Dojo 1.7
  • August 2012 - Dojo 1.8
  • März 2013 - IBM Notes 9.0 mit Dojo 1.8
  • Mai 2013 - Dojo 1.9
  • ? - Dojo 2.0 (Roadmap-Diskussion)

Abgrenzung jQuery, Dojo, extJS

ExtJS / Sencha Touch

  • starke Trennung zwischen Daten und Präsentation
  • gemischte Lizenz: Open Source und kommerziell
  • viele verfügbare kommerzielle Komponenten
  • Support durch Hersteller Sencha

jQuery

  • größte Entwicklergemeinde
  • optisch sehr ansprechende jQuery-Plugins
  • Syntax gewöhnungsbedürftig
  • wenig Abstraktion vom Browser-DOM

Dojo

  • umfangreiches Toolkit für Web und Mobile
  • wenige Erweiterungen/Zusatzkomponenten im Netz
  • lässt sich durch jQuery optisch aufpolieren
  • First Class Citizen in XPages, Bestandteil des Frameworks

Neu in Dojo 1.8 - Web

dojox/calendar (Demo)

(noch Probleme mit der Skalierbarkeit)

Neu in Dojo 1.8 - Web

dojox/dgauges (Demo)

Neu in Dojo 1.8 - Web

dojox/treemap (Demo)

Neu in Dojo 1.8 - Mobile

28 neue Widgets:
Auswahldialoge, Fortschrittsbalken, Audio/Video, Scrollbereiche innerhalb von Seiten, Badge-Icons (Demo)

Neu in Dojo 1.8

  • erste Komponenten ohne Support für Firefox 3.6 und IE6
  • XPages im Notes Client R9 basiert weiterhin auf der Firefox 3.6-Engine!

Neu seit Dojo 1.6 (Notes 8.5.3)

  • noch mehr Widgets
  • Web: u.a. dgrid - flexible Tabellenkomponente
  • Mobile: 21 neue Widgets
    z.B. Popup-Dialoge (Opener, Tooltip), ComboBox, SpinWheel
  • mehr Informationen: Link

Neu seit Dojo 1.6 (Notes 8.5.3)

  • AMD: Asynchronous Module Definition
  • lädt Dojo-Klassen und eigenen Code, ersetzt dojo.require()
  • viele APIs umbenannt und reorganisiert für 2.0,
    alte APIs aber noch nutzbar
  • wesentlich kleinerer Dojo-Kern mit zuladbaren Modulen

Maqetta

  • freier WYSIWYG-Editor für Dojo-Anwendungen
  • Web und Mobile
  • In der Cloud und Server als Download verfügbar (Link)

Layouting im Web - deklarativ

Layout im Web - deklarativ

  • Layout in Form von HTML-Tags
  • per CSS-Stil anpassbar in Breite und Form
  • Vielzahl Layout-Container verfügbar,
    die sich verschachtelt lassen
  • hauptsächlich für statische Inhalte verwendet

dojox/layout/BorderContainer

  • gut geeignet als Basislayout
  • fünf Bereiche: top, left, right, bottom und center
  • lediglich center muss befüllt werden
  • Mehrere Widgets pro Bereich durch layoutPriority-Attribut (definiert Reihenfolge)

BorderContainer

design="headline", hier zwei Widgets für "right"

BorderContainer

dasselbe Layout mit design="sidebar":

BorderContainer

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript"
  src="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/dojo.js"
  data-dojo-config="parseOnLoad:true"></script>
<link rel="stylesheet" type="text/css"
  href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dijit/themes/claro/claro.css">
<link rel="stylesheet" type="text/css"
  href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/resources/dojo.css">
<style>
html,body {
  width:100%;height:100%;margin:0;overflow:hidden;
}
</style>
<script type="text/javascript">
  <!-- benötigte Widgets laden -->
    require( ['dijit/layout/BorderContainer',
    'dijit/layout/ContentPane'],
      function(BorderContainer, ContentPane) {
      }
    );
</script>
</head>
<body class="claro">
<!-- BorderContainer -->
<div id="mainContainer" data-dojo-type="dijit/layout/BorderContainer"
  style="width:100%;height:100%">

  <!-- enthaltene ContentPanes -->
  <div id="topPane" data-dojo-type="dijit/layout/ContentPane"
    data-dojo-props="region:'top'" style="height:30px">
      Top pane
  </div>

  <div id="leftPane" data-dojo-type="dijit/layout/ContentPane"
    data-dojo-props="region:'left',splitter:true" style="width:20%">
      Left pane
  </div>

  <div id="centerPane" data-dojo-type="dijit/layout/ContentPane"
    data-dojo-props="region:'center'">
      center pane
  </div>

  <div id="rightPane1" data-dojo-type="dijit/layout/ContentPane"
    data-dojo-props="region:'right',layoutPriority:1" style="width: 10%">
      1st right pane from right
  </div>

  <div id="rightPane2" data-dojo-type="dijit/layout/ContentPane"
    data-dojo-props="region:'right',layoutPriority:2" style="width: 10%">
      2nd right pane from right
  </div>

  <div id="bottomPane" data-dojo-type="dijit/layout/ContentPane"
    data-dojo-props="region:'bottom'">
      first bottom pane
  </div>

</div>
</body>
</html>

data-dojo-* Attribute

  • HTML5-konforme Attributnamen
  • data-dojo-config
    zentrale Dojo-Konfiguration
<script type="text/javascript"
  src="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/dojo.js"
  data-dojo-config="parseOnLoad:true"></script>

alternativ als globale Variable:

<script type="text/javascript">
dojoConfig={
  parseOnLoad: true
}
</script>
<script type="text/javascript" src="/path/to/dojo.js"></script>

data-dojo-* Attribute

  • data-dojo-type
    Klassenname des Widgets (Trenner " / " oder " . ")
  • data-dojo-props (optional)
    Eigenschaften des Widgets (vgl. API-Dokumentation)
<div id="topPane" data-dojo-type="dijit/layout/ContentPane"
  data-dojo-props="region:'top'" style="height:30px">
    Top pane
</div>

require

  • AMD-Befehl zum Laden von Klassen
  • je nach Modus blockierend oder asynchron
  • Ruft Callback auf, sobald Klassen verfügbar sind
  • notwendig, damit aus Tags Widgets werden können
<script type="text/javascript">
  <!-- benötigte Widgets laden -->
    require( ['dijit/layout/BorderContainer',
      'dijit/layout/ContentPane'],
        function(BorderContainer, ContentPane) {
          //Callback hat Zugriff auf geladene Klassen
        }
    );
</script>

Dojo-Parser

  • Parser kann auch manuell aufgerufen werden
  • parseOnLoad: false
  • Vorteil:
    Callback-Aufruf, sobald Widgets generiert sind
<script type="text/javascript">
  require( ['dijit/layout/BorderContainer', 'dijit/layout/ContentPane',
    'dojo/parser', 'dojo/ready'],
    function(BorderContainer, ContentPane, parser, ready) {
      //dojo/ready wartet bis DOM geladen ist
      ready(function() {
        parser.parse().then(function(arrWidgets) {
          //aufgerufen mit Array aller erzeugten Widgets
            for (var i=0; i<arrWidgets.length; i++) {
              console.log('Widget erzeugt: '+arrWidgets[i].id);
            }
        });
      });
  });
</script>

Andere Layout-Widgets

  • dijit/layout/ContentPane
    Container für beliebige DOM-Knoten
  • dijit/layout/AccordionPane
    mehrere Bereiche untereinander, einer ist sichtbar
  • dijit/layout/TabContainer
    Karteireiter
  • dojox/layout/ExpandoPane
    zuklappbare Sidebar
  • dojox/layout/GridContainer
    Portallayout

Portallayout

Portal mit zwei Portlets

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript"
    src="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/dojo.js"></script>
<link rel="stylesheet" type="text/css"
    href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dijit/themes/soria/soria.css">
<link rel="stylesheet" type="text/css"
    href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/resources/dojo.css">
<link rel="stylesheet" type="text/css"
    href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojox/widget/Portlet/Portlet.css">
<link rel="stylesheet" type="text/css"
    href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojox/layout/resources/GridContainer.css">
<style>
html,body {width:100%;height:100%;margin:0;overflow:hidden}
.gridContainerTable {border:0;height:inherit}
.gridContainer > div {height:inherit}
</style>
<script type="text/javascript">
    require( ['dijit/layout/BorderContainer',
    'dijit/layout/ContentPane', 'dojox/layout/GridContainer',
    'dojo/parser', 'dojo/ready',
    'dojox/widget/Portlet'],
    
    function(BorderContainer, ContentPane, GridContainer, parser,
    ready, Portlet) {
        ready(function() {
            parser.parse();
        });
    });
</script>
</head>
<body class="soria">
<div id="mainContainer" data-dojo-type="dijit/layout/BorderContainer"
  data-dojo-props="design:'sidebar'" style="width: 100%; height: 100%">

<!-- Portal im Center-Bereich anzeigen -->
<div id="portal" data-dojo-type="dojox/layout/GridContainer"
  style="height:100%" data-dojo-props="nbZones:3,region:'center',
  hasResizableColumns:true,isAutoOrganized:false,withHandles:true,
  dragHandleClass:'dijitTitlePaneTitle',colWidths:[20,60,20]">
  
  <!-- Erstes Portlet -->
  <div id="portlet1" data-dojo-type="dojox/widget/Portlet"
  data-dojo-props="title:'Portlet 1',column:1">
    Hello World.
  </div>

  <!-- Zweites Portlet -->
  <div id="portlet2" data-dojo-type="dojox/widget/Portlet"
  data-dojo-props="title:'Portlet 2',closable:false,column:2">
    Greetings from Entwicklercamp.
  </div>
</div>
</body>
</html>

Portallayout

3 Spalten, Portlets verschiebbar per Drag&Drop

Demo: Portallayout

Widgets erzeugen in JavaScript

  • Widget-Generierung in JavaScript
  • im Zusammenspiel mit Ajax sehr flexibel
  • Dojo-Widgets bilden eine Baumstruktur:
require(['dijit/registry', 'dijit/layout/ContentPane'],
  function(registry, ContentPane) {

  //Ergänzen von Widgets mit addChild()
  //main: vorhandener dijit/layout/BorderContainer
  var mainContainer=registry.byId('main');
  var newPanel=new ContentPane({
    id: 'contentpane1',
    content: 'Pretty dynamic content '+(new Date()).toString(),
    region:'bottom',
    splitter: true
  });
  mainContainer.addChild(newPanel);
  //resize berechnet Positionen neu
  mainContainer.resize();

  //Auslesen von enthaltenen Widgets mit getChildren()
  var arrChildren=mainContainer.getChildren();

  //Entfernen von Widgets mit removeChild()
  var childWidget=arrChildren[0];
  mainContainer.removeChild(childWidget);
  //falls nicht mehr benötigt, Freigabe aller Ressourcen
  childWidget.destroyRecursive();
}
					

Demo: Dynamische UI

Formular-Widgets

Formular-Widgets

Widgets können frei platziert werden, entweder deklarativ:

<body>
Lorem ipsum dolor sit amet.<br />
<input type="text" data-dojo-type="dijit/form/TextBox" /><br />
consetetur sadipscing elitr.
</body>

Formular-Widgets

...oder programmatisch:

require(['dojo/_base/window', 'dojo/dom', 'dijit/registry'],
  function(win, dom, registry) {
    //Ziel-DOM-Knoten mit ID 'targetDomNodeId' finden:
    var targetDomNode=dom.byId('targetDomNodeId');
    //Bestehendes Widget mit ID 'myWidgetId' finden:
    var myWidget=registry.byId('myWidgetId');
    //Widget-Position ändern
    //(first/last=erstes/letztes Kind,
    //before/after=vor/nach Referenz-Knoten)
    myWidget.placeAt(targetDomNode, 'first');
  }
});

Demo: Überblick Formular-Widgets

Link

Eine Sammlung wichtiger APIs:

( Bitte alles auswendig lernen! ;-) )

Wichtige Widget-APIs

Widget über dessen ID auffinden

// dijit/registry
var widget=registry.byId('widgetId1');

Lesen/Schreiben von Eigenschaften

var propValue=myWidget.get('value')
myWidget.set('value', 'xyz');

Wichtige DOM-APIs

DOM-Knoten über dessen id auffinden

// dojo/dom
var nodeWithId=dom.byId('nodeId1')

Lesen und Ändern von Stilen

// dojo/dom-style
var styleValue=domStyle.get(node, 'display');
domStyle.set(node, 'backgroundColor', '#ff0000');

Wichtige DOM-APIs

Lesen/Ändern von DOM-Knoten-Klassen

// dojo/dom-class
var nodeHasClass=domClass.has(node, 'myclass');
domClass.add(node, 'myclass');
domClass.remove(node, 'myclass');

Erzeugen und Platzieren von DOM-Knoten

// dojo/dom-construct
domConstruct.empty(refNode);
var createdNode=domConstruct.create('div',
  {innerHTML:'<b>Test</b>'}, refNode, 'first');
domConstruct.place(refNode, otherNode, 'before');

Abmessungen von DOM-Knoten

// dojo/dom-geometry
var box=domGeom.getMarginBox(node);
//box.width, box.height, box.top, box.left

Wichtige DOM-APIs

Finden von DOM-Knoten über CSS-Selector

// dojo/query
var arrNodes = query('.headline', bodyNode);

Wichtige Dojo-APIs

Sprachabhängige Formatierung eines Datums

// dojo/date/locale
var formattedDateStr=locale.format(myDate, {selector:'date'});

Konvertierung zwischen Date und ISO8601-Format

// dojo/date/stamp
var parsedDate=stamp.fromISOString(isoDateStr);
var dateAsString=stamp.toISOString(mydate);

Suche nach einem Wert in Arrays

// dojo/_base/array
var index=array.indexOf(myArray, valueToFind);

Wichtige Dojo-APIs

Parsen von JSON-Strings und Formatieren von Objekten als JSON

// dojo/json
var parsedJsonObj=JSON.parse("{'foo':'bar'}");
var jsonStr=JSON.stringify( {foo:'bar'} );

…und schließlich:

// dojo/_base/lang
// Objekt rekursiv klonen
var clonedObj=lang.clone(obj);

// Code in anderem Scope ausführen
var myfunction=lang.hitch(mygrid,
  function(key, value) {this.set(key, value);} );
myfunction('store', newStore);

// Datentypprüfung:
lang.isArray(x) / lang.isFunction(x) / lang.isObject(x) /
lang.isString(x)

// Textersetzung
lang.replace("Hello {name.first} {name.last}!",
  { name: {first:  "Rudi", last: "Knegt"} });

Events registrieren

Reagieren auf Ereignisse

Das neue dojo/connect

dojo.connect wurde aufgeteilt:

  • dojo/on - DOM-Events
  • dojo/aspect - Hooks für Klassenmethoden

dojo/on

"on" im Event-Namen wird gestrichen:

//Registrieren von Event-Code für onClick-Event:
//verwendet dojo/dom und dojo/on
var myDomNode=dom.byId('nodeId');
var signal=on(myDomNode, 'click',
  function(evt) {alert('Node clicked'); });
  
//Entfernen von Event-Code:
signal.remove();

Event-Delegation

Abfangen von 'hochbubblenden' Events in Eltern-Containern

<div id="parentDiv">
  <button id="button1" class="clickMe">Click me</button>
  <button id="button2" class="clickMe">Click me also</button>
  <button id="button3" class="clickMe">Click me too</button>
  <button id="button4" class="clickMe">Please click me</button>
</div>
<script>
require(["dojo/on", "dojo/dom", "dojo/domReady!"],
  function(on, dom, domReady) {
 
    var myObject = {
      id: "myObject",
      onClick: function(evt){
        alert("The scope of this handler is " + this.id);
      }
    };
    var div = dom.byId("parentDiv");
    on(div, ".clickMe:click", myObject.onClick);
  });
</script>

dojo/aspect

  • Aspektorientierte Programmierung
  • aspect.before, aspect.after und aspect.around
require(['dojo/aspect', 'dojo/json', 'dojo'],
  function(aspect, JSON, dojo) {
  	
  //veraltete Funktion 'dojo.xhrGet()' überschreiben
  aspect.around(dojo, "xhrGet", function(originalXhr) {
    return function(args) {
      var t0=new Date().getTime();
      
      //Originalfunktion aufrufen
      var deferred = originalXhr(args);

      //wir führen ein Callback zum Deferred-Objekt hinzu
      deferred.then(function(data) {
        var t1=new Date().getTime();
        console.log("Data read in "+(t1-t0)+
          "ms with request: "+JSON.stringify(args));
      });

      //und geben es an den Aufrufer weiter
      return deferred;
    }
  });
});

dojo/topic

  • Publish/Subscribe-Architektur
  • entkoppelt Sender und Empfänger von Nachrichten
  • verwendet für Drag and Drop-System:
    /dnd/start, /dnd/cancel, /dnd/drop
require(['dojo/topic'], function(topic) {
  topic.subscribe("some/topic", function(event){
    // Objekt 'event' verarbeiten
  });
  //neues Event-Objekt publizieren an alle Subscriber
  topic.publish("some/topic", {name:"My Birthday Party",
    location:"Karlsruhe"});
});

Arbeiten mit Daten

Webformulare und Ajax

Dojo-Formulare

  • dijit/form/Form vereinfacht Arbeit mit Webformularen
  • Lesen und Schreiben der Formulardaten als JS-Objekt
  • Validieren des Formulars mit myform.validate()

Dojo-Formulare

dijit/form/Form wird zu FORM-Tag im DOM:

<div id="myformid" data-dojo-type="dijit/form/Form">
  <table>
    <tr>
      <td>Firstname</td>
      <td><input type="text" name="firstname"
        data-dojo-type="dijit/form/TextBox" /></td>
    </tr>
    <tr>
      <td>Lastname</td>
      <td><input type="text" name="lastname"
        data-dojo-type="dijit/form/TextBox" /></td>
    </tr>
  </table>
</div>

Dojo-Formulare

Formulardaten lassen sich komplett abziehen und setzen:

require(['dojo/json', 'dijit/registry'],
  function(JSON, registry) {
    var myform=registry.byId('myformid');
    var formData=myform.get('value');
    
    //Formulardaten als JSON-String anzeigen:
    alert(JSON.stringify(formData));

    //Formularfelder aktualisieren, basiert auf
    //"name"-Attribute der Felder
    myform.set('value', {firstname:'Rudi', lastname:'Knegt'});

    //Formular validieren
    if (!myform.validate())
      alert('Formulardaten ungültig!');
  }
});

Demo: Formulardaten lesen/schreiben

Ajax-Requests

dojo/request/xhr

  • ersetzt dojo.xhrGet() / .xhrPost()
  • xhr-Paket umfasst auch Cross-Domain-Protokolle wie IFrame/JSONP
require(["dojo/request/xhr"], function(xhr) {
  xhr("myxagent.xsp", {
    handleAs: "json"
    method: "GET"
  }).then(function(data) {
        //Daten verarbeiten (hier JS-Objekt von JSON-Daten)
      }, function(err) {
        //Fehler melden bzw. loggen
      }, function(evt){
        //Fortschrittsinfo verarbeiten
        //(erfordert XHR2-Support im Browser)
      });
});

Demo: Notes-Ansichten mit dem LazyTreeGrid

AMD

Asynchronous Module Definition

AMD

  • Classloader für Dojo-Klassen und eigenen Code
  • experimentell in Dojo 1.6, Standard seit 1.7
  • neuer asynchroner Modus generiert Script-Tags statt xhr
  • besserer Durchsatz im Browser als bei xhr
  • neue API-Aufteilung führt zu kleineren Code-Einheiten
  • kompaktere Custom Builds, aber mehr HTTP-Requests ohne Optimierung

Mehr HTTP-Requests?

Soviele können das doch nicht sein!

Das war unser Grid-Beispiel :-)

Lösungen

  • Custom Builds (Tutorial)
  • XPages-Runtime-Optimizer:
    "Use runtime optimized JavaScript and CSS resources"
  • fassen Dojo-Klassen und Abhängigkeiten zusammen

Asynchrones Classloading

Aktivierung über data-dojo-config:

<script type="text/javascript" src="path/to/dojo.js"
  data-dojo-config="async:true"></script>
  • async:false (Default)
    Laden von Legacy-APIs, z.B. dojo.connect, dojo.xhrGet etc.
  • async:true
    nur dojo-Nano-Kernel laden (klein)
  • Bei XPages in R9 leider immer async:false

Legacy-APIs

bei async:true können Legacy APIs als Modul "dojo" geladen werden:

require(['dojo'], function(dojo) {
  dojo.connect(...);
});

define

Eigene AMD-Module definieren

define

Syntax analog zu require:

  • Abhängigkeiten einbinden
  • Callback-Funktion wird nach Ladevorgang aufgerufen
  • Unterschied: Wir geben etwas zurück!
//Inhalt von "/subdir/db.nsf/dojo/mindoo/tools/XspUtil.js":
					
define(['dojo/query'], function(query) {
  //einfaches JS-Objekt mit einer Methode zurückgeben
  return {
    //getClientId findet DOM-Knoten, deren ID mit sComponentId endet
    getClientId: function(sComponentId) {
      var arrNodes=query("[id$='"+sComponentId+"']");
      if (arrNodes && arrNodes.length>0) {
        var sId=arrNodes[0].id;
        return sId;
      }
   }
  }
});

require

Verwenden des definierten Moduls:

  • Package-Ort bekanntmachen
  • Module/Klasse laden
<script type="text/javascript">
dojoConfig={
  packages:[
    { name:'mindoo', location:'/subdir/db.nsf/dojo/mindoo' }
  ]
}
</script>
<script type="text/javascript" src="path/to/dojo.js"></script>
<script type="text/javascript">
  //Klassenname des neuen Modus an require übergeben:
  require(['mindoo/tools/XspUtil', 'dojo/domReady!'],
    function(XspUtil, domReady) {
      //neu definierte Methode aufrufen:
      var clientId=XspUtil.getClientId('mytextfield');
      alert(clientId); // view:_id1:mytextfield
  });
</script>

define für Fortgeschrittene

Vererbung von Klassen mit define:

define(['dojo/_base/declare', 'dojox/data/QueryReadStore'],
  function(declare, QueryReadStore) {
    //Klasse "mindoo/data/QueryReadStoreExt" von
    //"dojox/data/QueryReadStore" ableiten
    //1. Parameter mit Klassenname in declare() ist optional,
    //wird globale Variable:
    return declare("mindoo/data/QueryReadStoreExt", [QueryReadStore], {

       fetch:function(request) {
         request.serverQuery = {q:request.query.name};
        
         //Methode der Elternklasse aufrufen:
         return this.inherited("fetch", arguments);
       }
    });
});

Ergebnis kann per require geladen werden und mit new instanziiert.

Laden von Dateien

  • AMD-Spezialmodul "module" liefert Pfad zur aktuellen Klasse
  • AMD-Plugin "dojo/text" lädt Dateien per Ajax
require(['module', 'dojo/dom'],
  function(module, dom) {
    var moduleUri=module.uri;
  
     //Pfad zu template berechnen
     var templatePath=moduleUri+"/../templates/mytemplate.html";
   
     //HTML-Datei laden
     require(['dojo/text!' + templatePath], function(template) {
        dom.byId('content').innerHTML = template;
     });
});

Parsen von Template und Wertersetzung z.B. über
Django Template Language dojox/dtl

Das hat Potenzial!

Demo: Activity Stream in NSF

Integration in XPages

Widgets verwenden

Dojo-Option "parseOnLoad" über Dojo-Panel der XPage aktivieren:

Widgets verwenden

Benötigte Dojo-Klassen im Ressourcen-Tab eintragen:

Notation mit " . " in R9 Beta hier erforderlich, um JS-Optimizer verwenden zu können

Widgets verwenden

  • Tags mit Dojo-Typ und -Attributen ausstatten
  • für Layout-Widgets wahlweise xp:div oder xp:panel

Widgets verwenden

Analoges vorgehen für Felder:


Eigene Klassen verwenden

Package-Location definieren über
"Resources / Add / Dojo Module Path Resource",
dann Klasse als "Dojo Module" aufnehmen

Eigene Klassen verwenden

Ort der Dojo-Datei im DB-Design (Package-Explorer)

Partial-Refresh-feste Oberflächen

  • Partial Refresh sendet Feldinhalte an Server, dieser berechnet Seitenbereich neu
  • Rückgabe-HTML wird in Seite eingefügt und auf Dojo-Widgets-Markup geprüft
  • führt ggf. zu Zurücksetzen von Widget-Eigenschaften:
    aktiver Reiter, Scrollposition in BorderContainer etc.
  • Lösung: Partial Refresh selektiv einsetzen oder gar nicht

Endlich lesbare Dojo-Quelltexte!

  • neue Eigenschaft in Notes/Domino R9:
    "Use uncompressed resource files (CSS & Dojo)"
  • Gold wert beim Debuggen von Problemen

Vielen Dank!

Zeit für Fragen!

Ressourcen

Argumente für Dojo

Dojo Features

Ressourcen

Dojo Features

Dojo Mobile 1.7 und 1.8

Ressourcen

Dojo 2.0

Layouts

Dojo-Forms

Ressourcen

Event-Handling

Drag and Drop

AMD

Ressourcen

AMD

Special effects

Mobile Scrollpane

Ressourcen

Alternative UI-Komponenten

Custom Builds

Ressourcen

Sonstiges