Voici un sujet pour lequel je n'ai jamais réussi à trouver d'informations auprès de mon ami "Google". Après pas mal de tâtonnement, j'ai, il me semble, trouvé une solution à peu près convenable.
La notion de "component" sous Knockout
J'ai décidé de me pencher sur ce sujet lorsque j'ai découvert la notion de "component" sous Knockout. Cette notion permet de faire des développements Javascript modulaires, ce qui signifie plus de souplesse, plus facile à maintenir... voici un lien vous présentant un peu le sujet : http://knockoutjs.com/documentation/component-overview.html.
Quel est le lien avec RequireJS ? La modularité. Comme je le disais plus haut, vous développez de façon modulaire, chaque module peut être lié à d'autres modules. Comment gérer ces liens et comment faire en sorte que lorsque vous décidez d'utiliser un "component" tous les "components" liés soient bien récupérer par le client ?
C'est là que la puissance de RequireJS prend toute son ampleur.
Le chainage des "components" avec RequireJS
Sur chaque "component" vous allez définir les autres "components" devant être récupérés pour pouvoir fonctionner. Chacun des "components" récupérés ayant eux-mêmes d'autres "components" de référencés. Il se crée ainsi un chainage entre "components". Vous n'avez plus besoin de vous inquiéter si telle ou telle librarie JS a bien été intégrée dans le "header", tout ce fait automatiquement et seuls les éléments nécessaires sont récupérés.
Chargement de RequireJS dans DNN
RequireJS est donc essentiel pour un développement JS modulaire. Maintenant il ne nous reste plus qu'à le charger sur notre site DNN mais voilà...ce n'est pas si simple que cela : RequireJS ne doit être chargé qu'une seule fois.
Sous DNN nous avons l'habitude travailler avec des modules qui sont indépendants les uns des autres, nous aurions donc pu imaginer utiliser RequireJS dans chaque module...malheureusement cela n'est pas possible.
RequireJS n'est pas une librairie JS comme les autres, en effet, lors de la mise en place de la balise SCRIPT permettant de charger RequireJS il faut faire référence à un fichier de configuration qui sera exécuté une fois RequireJS chargé sur le client. On ne peut définir qu'un seul fichier de configuration par page. Donc imaginer monter une instance de RequireJS par module n'est pas possible (où en tout cas je n'ai pas trouvé comment le faire).
Un SKINOBJECT pour charger RequireJS
Je suis donc parti sur la création d'un SKINOBJECT pour charger sur mes pages ma librairie RequireJS. Ce SKINOBJECT est ensuite déposé sur les SKIN où je souhaite utiliser cette librairie.
Voici donc le code C# de mon SKINOBJECT :
public partial class View : DotNetNuke.UI.Skins.SkinObjectBase
{
protected override void OnInit(EventArgs e)
{
LiteralControl javascriptRef = new LiteralControl("<script type='text/javascript' data-main='/PathToConfigFile/default.js' src='/PathToLibrary/require.js'></script>");
Page.Header.Controls.Add(javascriptRef);
}
}
Ici, j'insère une balise script dans le "Header" de ma page. Cette balise script pointe vers deux fichiers : "default.js" et "require.js".
Le premier ("default.js"), correspond au fichier de configuration exécuté par RequireJS dès son chargement. Vous trouverez plus loin un descriptif du contenu de ce fichier.
Le second ("require.js") est ni plus ni moins que la librairie RequireJS.
Voici le contenu du fichier "default.js" :
require.config({
baseUrl: '/DesktopModules',
paths: {
"knockout": 'PathToKnockout/knockout',
},
});
require(["knockout"], function (ko) {
$(document).ready(function () {
$.event.trigger("knockoutReady")
setTimeout(function () {
ko.applyBindings();
}, 10);
});
});
Ce fichier de configuration permet de définir l'URL de base de l'ensemble des chemins utilisés dans RequireJS par la suite ainsi que le chemin d'accès à la librairie "Knockout" (ce chemin étant relatif à la baseUrl s'il ne commence pas par "/").
La seconde partie de ce fichier, permet de mettre en place le Binding de Knockout. Pour cela j'utilise 2 astuces :
- je lève un événement "knockoutReady" que j'utiliserais dans mes modules utilisant des "components" Knockout pour charger ces derniers.
- je fais un "applyBindings" en asynchrone pour "binder" les viewModels de mes futurs modules.
Avec cela je suis fin prêt pour créer mes modules avec des "components" dans tous les sens.