Tutoriel original par Cyril Berthenet
Pour bien commencer…
Avant de commencer à implémenter l’exemple présenté dans ce tutoriel, il vous faut tout d’abord :
- Avoir fait le tutoriel « mon premier widget Dojo » (où au moins avoir télécharger le code source du tutoriel)
- # Copier les librairies dijit, dojo et dojox du framework Dojo dans le répertoire tutoDojo
Télécharger le framework Dojo à cette adresse http://download.dojotoolkit.org/ - Créer ensuite le répertoire exemple2 en effectuant un simple copier/coller du dossier exemple1.

- Editer les fichiers Search.js et searchWidget.html pour modifier le namespace
Search.js
/**********************************************//* *//* Widget : Search *//* Author : Berthenet Cyril *//* *//**********************************************/
dojo.provide("exemple2.widget.Search");
dojo.require("dijit._Templated");
dojo.require("dijit._Widget");
dojo.require("dijit.form.Button");
dojo.require("dijit.Menu");
dojo.requireLocalization("exemple2", "Locale");
dojo.requireLocalization("exemple2", "Ressources");
/* Class: Search */dojo.declare("exemple2.widget.Search", [dijit._Widget,dijit._Templated], {
/* Object: _nlsstockage du wording et des ressources du widget */_nls:{locale:null,ressources:null},
/* String: templatePathchemin d'acces vers le template du widget */templatePath: dojo.moduleUrl("exemple2.widget", "template/Search.html"),
/* Object: _dropdownBouton de selection du moteur de recherche */_dropdown:null,
/* Array: _enginesListe des moteurs de recherche */_engines:null,
/* Function: constructorConstructeur */constructor: function(){
console.log("exemple2.widget.Search::constructor");
this._nls.locale=
dojo.i18n.getLocalization("exemple2","Locale").searchWidget;
this._nls.ressources=
dojo.i18n.getLocalization("exemple2","Ressources").searchWidget;
this._engines=this._nls.locale.engines;
this._dropdown={};
},
/* Function: destroyDestructeur */destroy: function(){
console.log("exemple2.widget.Search::destroy");
this.inherited("destroy",arguments);
if(this._dropdown){this._dropdown.destroy();}
delete this._nls;
delete this._engines;
},
/* Function: startupMethode publique appelee apres l'instanciation du widget et la creation de tousses noeuds DOM dans document.body */startup: function(){
console.log("exemple2.widget.Search::startup");
this.inherited("startup",arguments);
this.searchField.value=(this.text==null||this.text=="")?
this._nls.locale.text:this.text;
var menu = new dijit.Menu();
dojo.forEach(this._engines, function(engine){
var menuItem = new dijit.MenuItem({
label: engine,
onClick: dojo.hitch(this, function(param) {
this._dropdown.attr("label",param);
this._dropdown.attr("engine",param);
},engine)
});
menu.addChild(menuItem);
},this);
this._dropdown = new dijit.form.DropDownButton({
label: this._engines[0],
engine: this._engines[0],
dropDown: menu}, dojo.byId(this.id+"Selector"));
this._dropdown.startup();
},
/* Function: _onKeyPressMethode privee executee lors de la saisie d'un caractere dans le champs de recherche */_onKeyPress: function(/*Object*/evt) {
console.log("exemple2.widget.Search::_onKeyPress");
if(evt.keyCode == dojo.keys.ENTER){
this._search();
dojo.stopEvent(evt);
return;
}},
/* Function: _clearMethode privee permettant d'effacer le texte du champs de saisie */_clear: function() {
console.log("exemple2.widget.Search::_clear");
if(this.searchField.value==this._nls.locale.text||this.searchField.value==this.text) {
this.searchField.value="";
}},
/* Function: _searchMethode privee executant la recherche */_search: function() {
console.log("exemple2.widget.Search::_search");
if( this.searchField.value!=this._nls.locale.text &&
this.searchField.value!=this.text &&
this.searchField.value != "") {
var url = "";
var fieldValue = this.searchField.value;
var engines = this._nls.ressources.engines;
switch(this._dropdown.attr('engine')){
case this._engines[0]:
url = engines.google;
break;
case this._engines[1]:
url = engines.yahoo;
break;
case this._engines[2]:
url = engines.wikipedia;
break;
}dojo.byId("viewer").src=url+escape(fieldValue);
}else {
var dialogbox = dijit.byId("dialogbox");
var content = this._nls.ressources.popup;
dialogbox.attr("title",this._nls.locale.popup.title);
dialogbox.attr("content",dojo.string.substitute(content,{
message:this._nls.locale.popup.message,
btnLabel:this._nls.locale.popup.btnLabel
}));
dialogbox.show();
}}
});
searchWidget.html
<script type="text/javascript">// <![CDATA[
dojo.require("dojo.i18n");
dojo.require("dijit.Dialog");
dojo.require("exemple2.widget.Search");
dojo.addOnLoad(init);
function init(){
var _search = new exemple2.widget.Search({
id:"search",
text:"saisir votre texte ici..."
}, dojo.byId("search"));
_search.startup();
var dialog = new dijit.Dialog({
title: "title"
}, dojo.byId("dialogbox"));
dialog.startup();
}// ]]></script>
Conventions de codage adoptées dans ce tutoriel
- commentaires : /* commentaire sur une ou plusieurs lignes */
- ajout d’une ligne console.log en début de fonction pour identifier les méthodes appelées lors de l’exécution du code
- les méthodes privées sont préfixées par un underscore
- afin d’éviter les problèmes d’encodage, les caractères accentués sont proscrit du code Dojo
Définition du template du widget ‘Viewer’
Ce composant graphique va nous permettre d’afficher le résultat de la recherche sous forme d’une page HTML.
Il est composé des éléments suivants :
- un div container qui nous permettra par la suite de définir le style de notre widget
- d’une iframe pour l’affichage
Viewer.html
<div class="viewer">class="iframe"<br /> src="${_nls.ressources.defaultPage}"<br /> dojoAttachPoint="viewer"<br /> scrolling="yes"<br /> frameborder="no"><br /></div>
lignes :
4 : url de la page qui sera affichée par défaut dans le viewer (gestion du lien dans les ressources)
Modification des ressources
nls/Ressources.js et nls/fr/Ressources.js
},
"viewerWidget" : {
"defaultPage": "static/empty.html"
}
Définition du style du widget ‘Viewer’
viewer.css
.viewer .iframe {
width: 800px;
height: 400px;
border:3px solid #B0B0B0;
}
Modification de la page statique
empty.html
Le résultat de votre recherche s'affichera dans ce viewer
Définition de la classe ‘Viewer’
Diagramme de classe
Viewer
- _nls : object
- templatePath : string
- _topics : array
+ constructor()
+ destroy()
+ postCreate()
+ setContent()
+ reset()
Viewer.js
/**********************************************//* *//* Widget : Viewer *//* Author : Berthenet Cyril *//* *//**********************************************/
dojo.provide("exemple2.widget.Viewer");
dojo.require("dijit._Templated");
dojo.require("dijit._Widget");
dojo.requireLocalization("exemple2", "Ressources");
/* Class: Search */dojo.declare("exemple2.widget.Viewer", [dijit._Widget,dijit._Templated], {
/* Object: _nlsstockage du wording et des ressources du widget */_nls:{ressources:null},
/* String: templatePathchemin d'acces au template du widget */templatePath: dojo.moduleUrl("exemple2.widget", "template/Viewer.html"),
/* Function: constructorConstructeur */constructor: function(){
console.log("exemple2.widget.Viewer::constructor");
this._nls.ressources=
dojo.i18n.getLocalization("exemple2","Ressources").viewerWidget;
},
/* Function: destroyDestructeur */destroy: function(){
console.log("exemple2.widget.Search::destroy");
delete this._nls;
},
/* Function: setContentModification du contenu du viewer */setContent: function(/*String*/url) {
console.log("exemple2.widget.Viewer::setContent");
this.viewer.src=url;
},
/* Function: resetRemise a zero du contenu du viewer */reset: function() {
console.log("exemple2.widget.Viewer::setContent");
this.setContent(this._nls.ressources.defaultPage);
}
});
lignes :
30 et 31 : chargement des ressources du widget
43 à 46 : méthode permettant de modifier le contenu de l’iframe avec une url passée en paramètre
50 à 53 : remise à zéro du contenu du viewer par l’affichage de la page par défaut
Création d’un sample de test
viewerWidget.html
<script type="text/javascript">// <![CDATA[
var djConfig = {
parseOnLoad: false,
isDebug: true
};
// ]]></script><script src="../dojo/dojo.js" type="text/javascript"></script>
<script type="text/javascript">// <![CDATA[
dojo.require("dojo.i18n");
dojo.require("exemple2.widget.Viewer");
dojo.addOnLoad(init);
function init(){
var _viewer = new exemple2.widget.Viewer({
id:"viewer"
}, dojo.byId("viewer"));
_viewer.setContent("http://www.google.fr");
}// ]]></script>
lignes :
8 : chargement du style
31 à 33 : instanciation du widget « Viewer »
35 : affichage par défaut du moteur google
Définition de la classe de notre application
Nous avons fait le choix dans ce tutoriel de construire notre exemple à partir d’une classe appelant un template regroupant les widgets de base de notre application. Les widgets qui pourraient avoir d’autres fonctions que celle de la rechercher dans une application plus complète seront déportés vers le sample de test (ex: la boîte de dialogue)
MonAppli.js
/**********************************************//* *//* Widget : Mon application *//* Author : Berthenet Cyril *//* *//**********************************************/
dojo.provide("exemple2.widget.MonAppli");
dojo.require("dijit._Templated");
dojo.require("dijit._Widget");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.Dialog");
dojo.require("exemple2.widget.Search");
dojo.require("exemple2.widget.Viewer");
dojo.requireLocalization("exemple2", "Ressources");
/* Class: Search */dojo.declare("exemple2.widget.MonAppli", [dijit._Widget,dijit._Templated], {
/* Boolean: widgetsInTemplateaffichage de widgets dans un template (false par defaut) */widgetsInTemplate: true,
/* String: templatePathchemin d'acces au template du widget */templatePath: dojo.moduleUrl("exemple2.widget", "template/MonAppli.html"),
/* Function: constructorConstructeur */constructor: function(){
console.log("exemple2.widget.MonAppli::constructor");
}
});
ligne :
27 : la variable widgetsInTemplate est très importante car elle permet l’interprétation des widgets à l’intérieur d’un template
Définition du template de notre application
Le template de notre application regroupe les composants graphiques permettant la mise en page (dijit.layout) et ceux liés à la fonction de recherche (Search et Viewer).
MonAppli.html
<div>
<div>
</div>
<div>
</div>
</div>
Distribution des données dans l’application
Les widgets publient leurs données et/ou s’abonnent pour en obtenir.
remplacer la ligne :
Search.js
dojo.byId("viewer").src=url+escape(fieldValue);
par :
Search.js
dojo.publish("displayResult",[url+escape(fieldValue)]);
Viewer.js
/* Array: topics */_topics:null,
/* Function: constructorConstructeur */constructor: function(){
console.log("exemple2.widget.Viewer::constructor");
this._nls.ressources=dojo.i18n.getLocalization("exemple2","Ressources")
.viewerWidget;
this._topics=[];
},
/*Function: destroyDestructeur */destroy: function(){
console.log("exemple2.widget.Search::destroy");
delete this._nls;
delete this._topics;
},
/* Function: postCreateMethode appelee apres la creation du widget */postCreate: function() {
this._topics[0]=dojo.subscribe("displayResult", this, "setContent");
},
lignes :
27 : définition d’un tableau pour le stockage des topics.
35 : initialisation du tableau de topics.
43 : destruction du tableau de topics.
48 à 50 : méthode appelée automatiquement après la création du widget.
49 : déclenchement de la méthode setContent() du Viewer lorsque un topic « displayResult » est publié dans l’application.
note : le widget ‘Search’ envoi un topic « displayResult » avec l’url de recherche dans toute l’application. Le widget ‘Viewer’ qui a souscrit à ce topic va exécuter sa méthode setContent() avec l’url passée en argument du topic
Modification des styles
Séparation des styles propres à chacun des widgets et ceux pouvants être réutilisé dans toute l’application. On a préféré faire ici plusieurs fichiers css qui seront compressés en un seul fichier css par le processus de buid.
search.css
.search {
height:30px;
}.search .btn {
background: url(../images/loupe.png) no-repeat;
width:20px;
height:19px;
border-left:0px;
cursor:pointer;
}.search input{
height:21px;
font:13px verdana, sans-serif;
}
monappli.css
.floatLeft {
float:left;
}.greyBorder {
border:1px solid #B0B0B0;
}
/****************************************//* Surcharge theme nihilo de Dojo *//****************************************/
.nihilo .dijitDropDownButton .dijitReset {
height:16px;
}.nihilo .dijitDropDownButton {
float: left;
margin:0 !important;
}.nihilo .dijitDropDownButton span {
font:10px verdana, sans-serif;
}.nihilo .dijitButtonNode {
width:80px;
border:1px solid #B0B0B0 !important;
border-right:0px !important;
}
Test
monApplication.html
<!-- @import "../dijit/themes/dijit.css"; @import "../dijit/themes/nihilo/nihilo.css"; --><script type="text/javascript">// <![CDATA[
var djConfig = {
parseOnLoad: false,
isDebug: true
};
// ]]></script><script src="../dojo/dojo.js" type="text/javascript"></script>
<script type="text/javascript">// <![CDATA[
dojo.require("dojo.i18n");
dojo.require("exemple2.widget.MonAppli");
dojo.addOnLoad(init);
function init(){
var _appli = new exemple2.widget.MonAppli({}, dojo.byId("appli"));
var dialog = new dijit.Dialog({}, dojo.byId("dialogbox"));
dialog.startup();
}// ]]></script>
lignes :
8 : chargement du style du widget ‘Viewer’
9 : chargement du style du widget ‘Search’
10 : chargement des styles communs partagés par toute l’application
12 à 15 : chargement des thèmes dijit et nihilo pour la création du bouton à menu déroulant
38 : instanciation de l’application
40 : instanciation de la boîte de dialogue pour l’affichage des erreurs. On aurait pu inclure ce widgets dans le template de notre application en imaginant qu’il puisse avoir une autre fonction que l’affichage des erreurs liées à la recherche dans une application plus complète.

Berthenet Cyril
http://www.life-behind-the-mirror.com/codes/tutoDojo/myFirstDojoAppli.php