Anfang Mai luden Uxebu und SitePen zu einem Dojo Toolkit-Workshop, genauer gesagt „Intro to Dojo, Charts, Grids, and Comet“, nach Oberhaching bei München. Mayflower war mit schätzugsweise einem guten Drittel aller Workshop-Teilnehmer zahlreich vertreten.
Dylan Schiemann von SitePen, Inc. und Nikolai Onken von Uxebu führten durch das Programm, außerdem konnten die Teilnehmer bei Fragen auf das Wissen und Erfahrung von Wolfram Kriesing (Uxebu), Tobias von Klipstein (Uxebu) und Peter Higgins (Dojo Toolkit) zurückgreifen.
Nachdem das Programm im Laufe der Zeit auf einen Tag reduziert worden war, änderte sich auch das Grundkonzept der Veranstaltung. Diese sollte nun die Benutzung von einigen ausgewählten Widgets zeigen, wobei sich die Auswahl an der Anwendung Stocker orientierte.
http://persevere.sitepen.com/stocker.html
Das Backend (Persevere/CometD-basiert) wurde von Sitepen bereitgestellt. Nähere Details zum Backend bzw. zur Anwedung selbst können hier in Erfahrung gebracht werden:
http://www.sitepen.com/blog/2009/04/01/stocker-advanced-dojo-made-easy/
Die behandelten Widgets waren u.a.:
dijit.layout.BorderContainer
dijit.layout.ContentPane
dojox.grid.DataGrid (mit Anbindung an einen JSON-Store über dojo.data.ItemFileReadStore)
dojox.charting.Chart2D
Aufgrund des engen Zeitrahmens wurde auf den Dojo Parser sowie die CSS-Struktur nur insofern eingangen, als dass man sich die entsprechenden Includes kopieren/abschreiben konnte, mit der man eine Umgebung zum Ausprobieren hatte. Auch wurde nahezu ausschließlich nur das „deklarative“ Dojo-Programmierungsmodel gezeigt, sprich die Widgets wurden über Markup platziert und initialisiert, nicht programmatisch.
http://dojocampus.org/content/2009/04/15/declarative-vs-programatic/
Andererseits ist es wohl genau der Weg, den jemand beim ersten Blick auf Dojo zuallererst einschlagen würde.
Der Ablauf jedes Agenda-Teilschrittes (im Falle der Widgets) war wie folgt:
- Vorstellung des Widgets inkl. Beispielcode
- 15-30 Minuten Zeit, in der die Teilnehmer das zuvor Gezeigte selber ausprobieren konnte. In dieser Zeit begutachteten die Tutoren immer wieder den Fortschritt bzw. standen für Fragen zur Verfügung
Natürlich bestand die Agenda nicht nur aus Widgets – diese wurden immer wieder mit anderen Programmpunkten aufgelockert – sei es durch Interessantes von Dylan (ob Geschichte des Dojo Toolkits oder kurze Erklärung von Polling und Comet), ganz banal Mittagessen oder (am Ende) Peters beeindruckende Einblicke in plugd (Erweiterung der Dojo-Basisbibliothek).
Trotz der Ausrichtung auf Anfänger, gab es auch zwischendurch kleinere Tipps, die man auch als erfahrener Dojo-Anwender nicht unbedingt gekannt hatte, z.B.:
formatter-Funktion in dojox.grid.DataGrid:
Diese dient der Formatierung der Werte in den einzelnen Grid-Spalten.
Das Beispiel würde alle Werte in der linken Grid-Spalte in runden Klammern darstellen.
(Anm.: Folgender Code ist auf das zum Nachvollziehen Nötigste zusammengefasst)
<script type="text/javascript"> function customFormatter(value) { return "(" + value + ")"; } </script> <table dojoType="dojox.grid.DataGrid" store="..."> <thead> <tr> <th field="db_table_column_a">Left column</th> <th field="db_table_column_b" formatter="customFormatter">Right column</th> </tr> </thead> </table>
Eine relativ bequeme Möglichkeit, JavaScript in dijit.layout.ContentPane zu laden und auszuführen:
Um einzelne Seitenbereiche dynamisch nachzuladen bzw. upzudaten, bietet sich dijit.layout.ContentPane an. Man definiert also einen solchen Seitenbereich als ContentPane:
<div jsid="updatableContent" dojoType="dijit.layout.ContentPane" parseOnLoad="true"> </div>
Nach der Instanziierung kann man in JavaScript den Inhalt dieses Bereiches ganz einfach über den href-Parameter nachladen:
(Anm.: Dies geschieht dann per AJAX und unterliegt der same origin policy. Der Parameter parseOnLoad bewirkt hierbei, dass der nachgeladene Inhalt durch den Dojo Parser läuft und somit evtl. vorhandene Widgets gleich instanziiert werden. Dies ist in unserem Fall notwendig.)
dijit.byId("updatableContent").attr("href", "/some_content.php");
Netterweise ist z.B. dijit.Dialog von dijit.layout.ContentPane abgeleitet, d.h. die gleiche Vorgehensweise existiert auch da.
dijit.byId("myDialog").attr("href", "/content_for_my_dialog.php");
Dabei wird man jedoch auf folgendes Problem stoßen. Man wird vielleicht geneigt sein, im nachgeladenen Content nicht nur HTML und CSS auszuliefern, sondern auch JavaScript-Code, z.B.:
<script type="text/javascript"> alert("I've been loaded!"); </script> <p> Here be content. </p>
Jeglicher JavaScript-Code wird aber in diesem Fall entfernt werden. Ein möglicher Ausweg aus dieser Situation ist die Benutzung von dojox.layout.ContentPane, welche (richtige Parameter vorausgesetzt) das Nachladen von JavaScript erlaubt.
http://dojocampus.org/content/2008/07/30/executing-javascript-inside-content-panes/
Eine andere Möglichkeit besteht, JavaScript aus dem nachgeladenen Inhalt zu extrahieren und an die <head>-Node anzuhängen.
Um sich nicht selber um Extraktion etc. kümmern zu müssen, verpacken wir den JavaScript-Code in ein eigenes Widget, woraus er dann beim Parsen extrahiert wird und an die richtige Stelle im DOM wandert. Das Widget nennen wir z.B. dojo.Script
Das vorige Beispiel würde dann wie folgt aussehen:
<div dojoType="dojo.Script"> alert("I've been loaded!"); </div> <p> Here be content. </p>
Das Widget dojo.Script kann man z.B. auf die Schnelle durch folgenden Code im Programmfluss deklarieren, sobald dojo zur Verfügung steht.
(Anm.: Der folgende Code stammt von Peter Higgins)
(function(d){ var addCode = function(code, andAppend){ // pondering: can i do the andAppend check here? will the e.text/etc work if it's in the DOM first? var e = d.create("script", { type:"text/javascript" }), // jump through the cross-browser hoops: how = "text" in e ? "text" : "textContent" in e ? "textContent" : "innerHTML" in e ? "innerHTML" : "appendChild"; if(how == "appendChild"){ e[how](d.doc.createTextNode(code)); }else{ e[how] = code; } if(andAppend){ d.doc.getElementsByTagName("head")[0].appendChild(e); } return e; }; dojo.declare("dojo.Script", null, { // summary: A Widget to wrap around JavaScript to be injected into ContentPanes // to ensure they are executed. This is unneeded if the pane is a // dojox.layout.ContentPane // // description: // // example: // <div dojoType='dojo.Script'>alert('foo');<div> // constructor: function(args, node){ node = d.byId(node); if(node && node.innerHTML){ // hide the content asap d.style(node, "display", "none"); addCode(node.innerHTML, true); } } }); })(dojo);
Somit muss man sich zum einen nicht selber um nachgeladenes JavaScript kümmern und kann zum anderen immer noch auf dijit.layout.ContentPane und alle Widgets, die davon abgeleitet sind, zurückgreifen. Im Falle von dojox.layout.ContentPane z.B. müsste man sich erst einmal eine eigene Version von dijit.Dialog ableiten, sofern man dieses Widget mit JavaScript benutzen möchte.
Fazit: Der Besuch der Veranstaltung hat sich gelohnt! An dieser Stelle ein großes Dankeschön die Jungs von Uxebu und Sitepen.