Dojo toolkit

Alapok

Az első lépések

Töltsük le a legfrissebb verziót (http://dojotoolkit.org) és csomagoljuk ki egy könyvtárba. Alternatív megoldásként CDN (content delivery network) verziót is használhatunk.

  1. „Bootstrap” Dojo – töltsük be a dojo.js fájlt. Ez betölti a Dojo loadert, amivel majd további modulokat tölthetünk be.

    <script src="../dojo/dojo.js" data-dojo-config="async: true"></script>

    A data-dojo-config egy HTML 5 szerinti szabványos attribútum, amivel a Dojo-t konfigurálhatjuk. Ez property-k egy listája, és teljesen úgy működik, mint egy JavaScript objektum literál, csak nyitó és csukó zárójelek nélkül. Jelen esetben azt mondjuk meg, hogy aszinkron módban szeretnék használni a Dojo-t. Ez az 1.7-es verziótól, az AMD bevezetésétől kezdve lehetséges.

    Célszerű a body végére tenni a script elemet, mert különben lelassíthatja az oldalt a betöltődésével.

  2. Állítsuk be a <body> elemen a class-t (ez a dijit-nél lesz fontos)
  3. Töltsük be a szükséges modulokat, amiket használni szeretnénk.

Loader és modulok

A Dojo loader az a kód, aminek a segítségével betölthetünk más modulokat. Két API-t tartalmaz:

Az AMD formátum a Dojo 1.7-től támogatott, ez tulajdonképpen lecseréli a legacy loader API-t. Számos előnye van a régi modulstílushoz képest, például teljesen aszinkron, portábilis, jobb a függőségkezelése és könnyebben lehet debuggolni. Az AMD egy standard (community-driven), ami azt jeleni, hogy ha a specifikációnak megfelelően írjuk a moduljainkat, akkor azokat bármilyen másik AMD loader-el vagy könyvtárral használhatjuk. Más AMD implementációk például a RequireJS, curl és bdLoad.

Modulazonosítók

Az rögtön feltűnhet, hogy Dojo 1.7-től megváltozott a modulok azonosítása, már inkább egy elérési útra hasonlítanak, mint objektum referenciára, pl. a.b.c helyett a/b/c. Sokban hasonlítanak is az elérési utakra, például relatívan is megadhatjuk, hogy az adott package-ből mit szeretnénk használni: ha az a/b/c modul a ../d-re hivatkozik, akkor az az a/d lesz.

A loader konfigurálása

Három különböző módon is konfigurálhatjuk a loader-t:

Figyeljük meg, hogy az async flag-et csak az első két módon adhatjuk meg, a require függvénnyel nem. Van más tulajondság is, amit szintén nem adhatunk meg require-el, például a has.

Ha van dojoConfig (illetve visszafelé kompatibiliás miatt djConfig) és data-dojo-config attribútumunk is, akkor mindkettőt figyelembe veszi a loader, és a data-dojo-config-ban lévő értékek élveznek precedenciát.

Az AMD API

Az AMD a preferált loader API már, és két globális függvényen keresztül érhető el.

require
require( configuration, // konfiguráció (opcionális) dependencies, // függőségek: modulazonosítók listája (opcionális) callback // callback függvény (opcionális) ) -> undefined

A függőségeket a felsorolásuk sorrendjében oldja fel, majd argumentumként átadja őket a callback függvénynek. Példa:

require([ "dojo/dom" ], function(dom) { var node = dom.byId("myNode"); });

Konfigufárciós objektummal runtime átkonfigurálhatjuk a loader-t:

require({ baseUrl: "/js/", packages: [ { name: "dojo", location: "//ajax.googleapis.com/ajax/libs/dojo/1.8/" }, { name: "my", location: "my" } ] }, [ "my/app" ]);

Az AMD egyik másik előnye a régi modulformátummal szemben, hogy van benne cross-domain loading támogatás, szóval nyugodtan hivatkozhatunk másik domain-ből származó JavaScript fájlra is.

define

AMD modulokat definiálhatunk vele, a következő a szignatúrája:

define( moduleId, // modulazonosító (opcionális) dependencies, // függőségek: modulazonosítók listája (opcionális) factory // függvény vagy érték, amin keresztül a modulra hivatkozhatunk )

A moduleId opcionális, történelmi okoból szerepel az API-ban. Legjobban tesszük, ha nem is adjuk meg. Amíg egy forrásfájl pontosan egy modult definiál, addig a loader automatikusan ki tudja találni a module nevet.

A define-nak két fontos tulajdonsága van, ami nem feltétlen nyilvánvaló rögtön:

Mi is a különbség?
A require() dependencies és callback paraméterei pontosan ugyanúgy működnek, mint a define() dependencies és factory paraméterei:
require( ["dijit/layout/TabContainer", "bd/widgets/stateButton"], function(TabContainer, stateButton){ // do something } ); define( ["dijit/layout/TabContainer", "bd/widgets/stateButton"], function(TabContainer, stateButton){ // do something return definedValue; } );

Pluginok

A normál modulokon felül az AMD loader megkülönböztet egy speciális modultípust, a plugint. A pluginok azon kívül, hogy be lehet őket tölteni, kiegészítik a loadert plusz tulajdonságokkal. Többé-kevésbé ugyanúgy kell őket betölteni, mint a normál modulokat, de egy felkiáltójelet kell tenni a modulazonosító mögé, így felezve, hogy ez egy plugin. Minden, amit a felkiáltójel mögé teszünk, közvetlenül a pluginhoz kerül feldolgozásra. Számos elérhető plugin van, a legfontosabbak: dojo/text!, dojo/i18n!, dojo/has! és dojo/domReady!.

Példa: dojo/i18n!

require( ["dojo/i18n!myApp/nls/myResources"], function(myResources) { // myResources tartalmazza az i18n szövegeket } );

Ez a plugin detektálja a böngésző nyelvi beállítását és a megfelelő ún. resource bundle-el tér vissza. A felkiáltójel után megadott fájl (ebben az esetben myApp/nls/myResources.js) tartalmazza az alapértelmezett fordításokat és az egyéb nyelvekre vonatkozó támogatást.

Speciális modulazonosítók

Az AMD specifikáció három speciális modulazonosítót definiál:

require
// this is "myApp/topLevelHandlers" define(["dojo"], function(dojo) { dojo.connect(dojo.byId("debugButton"), "click", function() { require(["myApp/perspectives/debug"], function(perspective) { perspective.open(); }); }); });

Habár a fenti kód teljesen legális, lehet jobban is csinálni. Ha ./perspectives/debug-ot írunk a myApp/perspectives/debug helyett, sajnos ez még nem fog működni, mert a globális require() függvény nem tudja kezelni a relatívan megadott modulokat.

// this is "myApp/topLevelHandlers" define(["dojo", "require"], function(dojo, require){ dojo.connect(dojo.byId("debugButton"), "click", function(){ require(["./perspectives/debug"], function(perspective){ perspective.open(); }); }); });

Ha hozzáadjuk a require-t a függőségekhez, akkor már menni fog. Ekkor a require() hívás a lokális require-ön fog végrehajtódni, ami már képes a relatívan megadott modulokat kezelni, úgy is hívják, hogy context-sensitive require.

module

A module modul a következő tulajdonságokkal redelkező objektummal tér vissza:

exports

Az exports modul egy alternatív mód a modul definiálásra. Ahelyett, hogy a factory függvényben visszatérnénk egy értékkel, az exports biztosít egy objektumot, amiben beállíthatjuk, hogy mit szeretnék kiajánlani. Az alábbi két moduldefinició teljesen egyenértékű:

define([], function() { return { someProperty: "hello", someOtherProperty: "world" }; }); define(["exports"], function(exports) { exports.someProperty = "hello"; exports.someOtherProperty = "world"; });

Körkörös függőségek esetén az exports az egyetlen mód, ahogy helyesen definiálhatjuk a modulokat.

Nem ADM kód betöltése

A loader-el betölthetünk nem ADM kódot is, ha simán a fájl nevét adjuk át modulváltozóként. A loader ezt érzékeli (vagy "/"-el, vagy protokollal kezdődik a fájl, illetve .js-re végződik), és ilyen esetekben a modulváltozó értéke undefined lesz. Ebben az esetben közvetlenül érünk el mindent, ami szkriptünkben globálisan definiált.

A Dojo loader képes legacy és AMD modulok keverésére, ami nagyon hasznos tud lenni, ha szeretnénk áttérni AMD stílusra, de nem áll módunkban az egész kódbázist azonnal átírni. Ez működik szinkron és aszinkron módban is.

Aszinkron esetben a modulváltozó az lesz, amit a dojo.provide-ban megadunk:

// in "my/legacyModule.js" dojo.provide("my.legacyModule"); my.legacyModule = { isLegacy: true };

Ha az AMD loaderrel a require(["my/legacyModule"])-n keresztül hivatkozunk erre a kódra, a modulváltozó my.legacyModule lesz.