Dojo flip clock

January 20th, 2013 | 13 min read | CSS3, Dojo, JavaScript, Web

De laatste tijd ben ik enorm veel bezig met webtechnologieën en als JavaScript framework komt daar vaak Dojo bij te kijken. Dojo is eigenlijk de enterprise-variant van het enorm populaire jQuery. Als je in aanraking komt met IBM technologieën dan is de kans bestaande dat je ook met Dojo in aanraking komt. Producten zoals WebSphere Portal en WorkLight maken gebruik van Dojo.In deze tutorial ga ik een eigen Dojo widget schrijven, namelijk een flip clock. Ik ga hier enkel uitleg geven over de code zelf, de IDE kies je vrij (in mijn screenshots zal je merken dat ik Aptana Studios gebruik).

Project structuur

Voor zij die nog nooit gehoord hadden van Dojo en het eens willen uitproberen, Dojo valt te downloaden van http://dojotoolkit.org/. Zelf ga ik gebruik maken van Dojo gehost op een CDN.
Maak nu een directory aan voor je project, ik ga het

flipclock-example

noemen en plaats hierin een bestand met de naam

index.html

en een map

assets

. Ik gebruik zo’n

assets

map meestal om al mijn resource-gerelateerde bestanden in te plaatsen (afbeeldingen, stylesheets, JavaScript, …).

In de map

assets

maak je nu de map

css

en de map

js/my

aan. In de map

css

plaats je dan een bestand

flipClock.css

, in de map js plaats je een bestand

additions.js

en ten slotte plaats je in de map

js/my

een bestand

flipClock.js

en een bestand

flipClock.html

.

Je project structuur zou er dan moeten uitzien als in onderstaande afbeelding.

project-structure

Index HTML pagina

De volgende stap in het Dojo-verhaal is dat we een index-pagina gaan voorzien met de nodige code om zowel Dojo, de CSS als de extra toevoegingen te laden.

De code voor

index.html

is:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Flipclock example</title>

        <link href="assets/css/flipClock.css" rel="stylesheet" />
        <script type="text/javascript">
            dojoConfig = {
                parseOnLoad : true,
                async: true,
                packages: [
                    {
                        name: 'my',
                        location: location.pathname.replace(/\/[^/]+$/, '') + '/assets/js/my'
                    }
                ]
            }
        </script>
        <script src="//ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script>
        <script type="text/javascript" src="assets/js/additions.js"></script>
    </head>
    <body>
        <div data-dojo-type="my/flipClock" data-dojo-props="date: '2014-01-01T00:00:00Z'"></div>
    </body>
</html>

Er staat hier al heel wat (een deel werd automatisch gegenereerd door mijn IDE), maar waar we echt voor moeten opletten zijn de

<script>

-tags en de content in de

<body>

.

Zoals je kan zien hebben we een stuk inline JavaScript code die eigenlijk door de Dojo library gebruikt wordt om configuratie mogelijk te maken. Het is enorm belangrijk dat deze configuratie gebeurt voordat je de Dojo-library inlaadt (die staat in de

<script>

-tag eronder). Indien je dat niet doet zal Dojo de configuratie niet vinden en zal deze ook niet gebruikt worden.

Het belangrijkste binnen dit inline blok is dat

parseOnLoad

op

true

staat en de code binnen

packages

. Dojo werkt via een principe dat alle (of toch de meeste) widgets zowel via een JavaScript API geladen kunnen worden als via markup.
Het parsen van de markup kost echter veel moeite en tijd, en daardoor is er een functie die de parser noemt en waarmee je zelf kan kiezen wat je wanneer (en hoe) parst. In dit voorbeeld gaan we het echter eenvoudig houden en via

parseOnLoad

zeggen we gewoon dat als de pagina geladen is, de hele pagina geparsed moet worden.

Het tweede deel zijn de

packages

. Omdat we Dojo niet zelf gaan hosten maar gebruik gaan maken van een CDN (externe) host, moeten we duidelijk maken dat de

my

-package (waar de flip clock code terecht komt) niet te vinden is op de CDN host, maar lokaal.
Dit doen we door de naam van de package en de locatie mee te geven.

Dojo zelf laden we via een CDN cloud host, dit zorgt ervoor dat als je meerdere websites bezoekt die gebruik maken van deze CDN, de cache aangesproken kan worden en niet telkens opnieuw de JavaScript files gedownload moeten worden.

In de

<body>

-tag zie je de nodige markup-code die onze widget gaat gebruiken. Een dojo-widget via markup initializeren gebeurt via het

data-dojo-type

attribuut. Extra parameters aan je widgets meegeven doe je dan weer via

data-dojo-props

.

Stylesheet

Over de code in de stylesheet

flipClock.css

ga ik niet teveel spreken, dit is louter om het design van de klok een beetje mooier te maken, maar dit heeft uiteindelijk niets te maken met het Dojo/JavaScript gedeelte.

De code hiervoor is:

.flipClock .block {
    float: left;
    max-width: 150px;
}

.flipClock .block .digit {
    display: inline-block;
    float: left;
    min-width: 70px;
    padding: 25px 0px;
    height: 50px;
    background-color: #000;
    font-size: 50px;
    line-height: 50px;
    text-align: center;
    color: #FFF;
    border-radius: 5px;
    border: solid 1px #131313;
    -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 5px rgba(0,0,0,.15);
    -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 5px rgba(0,0,0,.15);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 5px rgba(0,0,0,.15);
    background-image: -moz-linear-gradient(top, #212121, #000 50%, #131313);
    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#212121), color-stop(.5, #000), to(#131313));
    background-image: -webkit-linear-gradient(top, #212121, #000 50%, #131313);
    background-image: -o-linear-gradient(top, #212121, #000 50%, #131313);
    background-image: linear-gradient(to bottom, #212121, #000 50%, #131313);
    background-repeat: repeat-x;
    text-shadow: 0 -1px 1px rgba(0, 0, 0, .5);
}

.flipClock .block .seperator {
    font-size: 50px;
    line-height: 50px;
    padding-top: 20px;
    font-weight: bold;
    display: block;
    float: left;
    height: 50px;
    margin: 0 auto;
    text-align: center;
}

.flipClock .block .name {
    display: block;
    width: 70px;
}

additions.js

Dojo maakt gebruik van een modulaire aanpak waarbij alle modules apart geladen moeten worden. Om gebruik te kunnen maken van de flipClock, zullen we eerst deze moeten “importeren”, dit doen we via de volgende code die je in

additions.js

plaatst:

/**
 * @author g00glen00b
 */
require(["my/flipClock"]);

De commentaar is niet zo belangrijk (is gegenereerd door mijn IDE), maar de code in de

require()

functie wel. Om een Dojo module te laden maak je gebruik van deze functie waarbij je een array van modules (in dit geval eentje) importeert.

Widget HTML template

Het leuke aan Dojo is dat je zo modulair kan werken en eigenlijk enorm veel zaken die je in een moderne OO-taal terug vindt (i18n, views/models) eigenlijk allemaal ook hier kan terug vinden.

Zo gaan we gebruik maken van een template voor de HTML code van onze widget die we in

flipClock.html

gaan plaatsen. De code hiervoor is:

<div class="${baseClass}">
    <div class="block">
        <span class="digit" data-dojo-attach-point="dayNode">00</span>
        <span class="seperator">:</span>
        <span class="name">Days</span>
    </div>
    <div class="block">
        <span class="digit" data-dojo-attach-point="hourNode">00</span>
        <span class="seperator">:</span>
        <span class="name">Hours</span>
    </div>
    <div class="block">
        <span class="digit" data-dojo-attach-point="minuteNode">00</span>
        <span class="seperator">:</span>
        <span class="name">Minutes</span>
    </div>
    <div class="block">
        <span class="digit" data-dojo-attach-point="secondNode">00</span>
        <span class="name">Seconds</span>
    </div>
</div>

Wat dit eigenlijk voorstelt is de HTML code die gebruikt moet worden op de plaats van de

<div>

die we op

index.html

gedefinieerd hadden.
Wat je hier voornamelijk moet opvallen is dat we op bepaalde plaatsen gebruik maken van het attribuut

data-dojo-attach-point

. Deze punten zijn enorm toegankelijk in de JavaScript code van de widget en zorgen ervoor dat je eenvoudig per widget de HTML code van deze attach points kan wijzigen.

Widget JavaScript code

Ten slotte nog het belangrijkste gedeelte van deze tutorial, de JavaScript code van de widget zelf. De code hiervoor ga ik hieronder plaatsen en daarna ga ik alles stuk voor stuk bespreken.

/**
 * @author g00glen00b
 */
require([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dojo/date",
    "dojo/html",
    "dojo/text!my/flipClock.html",
    "dojox/timing",

], function(declare, _WidgetBase, _TemplatedMixin, date, html, template, timing){

    declare("my/flipClock", [_WidgetBase, _TemplatedMixin], {
            templateString: template,
            baseClass: 'flipClock',

            _date: null,
            _setDateAttr: function(date) {
                this._set("_date", new Date(date));
            },
            _getDigit: function(_number) {
                return (_number < 10 ? '0': '') + _number;
            },
            _setDigits: function() {
                var _seconds = date.difference(new Date(), this._date, 'second');
                html.set(this.dayNode, this._getDigit(Math.floor(_seconds / 86400)));
                _seconds = _seconds % 86400;
                html.set(this.hourNode, this._getDigit(Math.floor(_seconds / 3600)));
                _seconds = _seconds % 3600;
                html.set(this.minuteNode, this._getDigit(Math.floor(_seconds / 60)));
                html.set(this.secondNode, this._getDigit(_seconds % 60));
            },

            postCreate: function() {
                this._setDigits();
                var obj = this;
                var _t = new timing.Timer(1000);
                _t.onTick = function() {
                    obj._setDigits();
                };
                _t.start();
            }
    });
});

Dit is al een iets complexere code dan wat we tot nu toe gezien hadden, maar op zich valt het allemaal nog wel mee als je iets van JavaScript kent.

Modules importeren

Als eerste stap zie je dat we net zoals voorheen enkele Dojo modules gaan importeren via de

require()

functie. Wat hier echter wel nieuw is, is dat we ook gebruik maken van een callback-functie die eigenlijk de code van die widgets terug geeft om ermee te kunnen werken.

De modules 

dojo/_base/declare

dijit/_WidgetBase

en

dijit/_TemplatedMixIn

zijn eigenlijk basiszaken die je nodig zal hebben om een widget te kunnen schrijven (en met een HTML template wilt kunnen werken).
De module

dojo/date

is dan weer een module waardoor we bewerkingen op data (verschil tussen twee data) kunnen maken. Deze module ga ik gebruiken om te bepalen hoeveel tijd er tussen nu en de de gegeven datum is.
Dan hebben we nog de module

dojo/html

die eigenlijk manipulaties op de DOM nodes toelaat. Zo ga ik via deze module de HTML code van een node wijzigen (hetzelfde als de

innerHTML

aanpassen).

Het volgende is echter geen module, maar is eigenlijk het laden van de HTML template zelf. De module die hiervoor verantwoordelijk is, is

dojo/text

en deze module kan eenvoudig dankzij de parameter achter het uitroepteken de juiste template laden.

Dan hebben we ten slotte nog de module

dojox/timing

die het mogelijk maakt om acties op basis van een interval mogelijk te maken (onze klok elke seconde updaten).

Widget object

Het volgende dat je ziet is een object binnen de

declare()

functie met allerlei properties. De property templateString is een vereiste van de

dijit/_TemplatedMixIn

module en zorgt ervoor dat we de widget kunnen koppelen aan de template.

De

baseClass

property wordt vaak gebruikt om de basis class-name in te geven. Als je nog eens goed kijkt naar de HTML template, zal je merken dat er bovenaan iets staat als

${baseClass}

. Die code is eigenlijk een placeholder voor een property in de widget en op die manier kan je het dus mogelijk maken om zaken mee te geven aan de template.

De

_date

en

_setDateAttr

properties horen samen en zijn eigenlijk het veld en de setter die gebruikt worden om de datum op te slaan die in de

data-dojo-props

gedefinieerd was. Als de index pagina geparsed wordt zullen de properties uitgelezen worden en zal automatisch de

_setDateAttr

functie aangeroepen worden.
In deze functie gaan we de datum-String omzetten tot een

Date

object en dat in

_date

plaatsen dankzij de

_set()

code.

De

_getDigit

property is niet zo belangrijk en zorgt er gewoon voor dat we van het cijfer ‘1’ iets kunnen maken als ’01’ voor de flip clock.

In de functie

_setDigits

gebeurt het echte werk. Hier gaan we het verschil bepalen tussen twee datums (via

date.difference()

en gaan we de juiste waarden koppelen aan de HTML nodes die we via

data-dojo-attach-point

in onze template hadden aangeduid. Zoals je kan zien zijn deze nodes (bijvoorbeeld

this.dayNode

) nu automatisch beschikbaar als een property binnen onze widget en kunnen we dus eenvoudig de HTML ervan aanpassen met

html.set

.

Ten slotte hebben we nog de property

postCreate

die altijd wordt aangeroepen nadat alle velden geïnitalizeerd zijn. Hier gaan we dus eigenlijk de code plaatsen die ervoor zorgt dat elke seconde de display geüpdate wordt (en er dus een seconde afgaat).
Dit doen we via een

timing.Timer

en die elke seconde via de

_t.onTick

property de functie

_setDigits

laten uitvoeren. We hebben hier echter wel een kleine workaround moeten maken omdat time-based functies in JavaScript na een keer z’n

this

-context verliest waardoor het eigenlijk geen kennis meer heeft van alle functies die we op

this

oproepen.
Om dit op te lossen slaan we de context op in

obj

en kunnen we deze telkens aanroepen via

obj._setDigits()

.

Uitvoeren

Met de code voor de widget zit de tutorial er dan ook op. Wat ons nu nog rest is alles uit te testen. Host de code, open

index.html

in je browser en verifieer het resultaat.

result

Vergelijking jQuery – Dojo

Dojo is naast jQuery niet zo populair bij huis-tuin-keuken-toestanden maar is meer gericht naar enterprise solutions. Dojo is meer gericht naar standaarden en het verschil merk je snel als je iets grotere applicaties schrijft.
Als je in jQuery iets extra nodig hebt zal je al snel een third party plugin moeten gebruiken waarvan je niet weet hoe lang deze nog gaat bestaan, hoeveel support er nog aangeboden wordt en hoe goed die code geschreven is.

Bij Dojo heb je een enorme waaier aan modules die sowieso al beschikbaar zijn (dijit, dojo en dojox), er wordt gewerkt met een duidelijke workflow, zo zal een dijit/dojo library deprecated kunnen zijn of niet terwijl een dojox library (dat zijn meer third party zaken) in de fase mature, maintained, experimental, abandoned of deprecated kan vallen.

Dit zorgt ervoor dat je toch zeker een bepaald toekomstbeeld kan gaan maken en niet rekening moet houden met plugins die over een jaar niet meer gaan werken.

Dojo heeft echter één groot nadeel en dat is dat de documentatie vaak niet aangevuld is, niet voldoende informatie biedt, niet bestaat of outdated is. Dit is het grootste punt van kritiek bij Dojo en sinds versie 1.8 is daar al enorm veel vooruitgang in geboekt met het oog naar de release van versie 2.0.

Download project

Download – flipclock-example.tar.gz (2 kB)

Demo

Back to tutorialsContact me on TwitterDiscuss on Twitter

Profile picture

Dimitri "g00glen00b" Mestdagh is a consultant at Cronos and tech lead at Aquafin. Usually you can find him trying out new libraries and technologies. Loves both Java and JavaScript.