mercredi 13 février 2013

Un outil de présentation en AngularJS

Pour une fois je ne vais pas vous détailler un aspect technique du framework, mais présenter une petite application qui peut être utile. Mais si je la présente ici, c'est bien sûr parce qu'elle est écrite avec AngularJS, et qu'il y a quelques exemples dans le code qui peuvent vous intéresser.

J'avais besoin d'un outil pour présenter des slides sur AngularJS en début de semaine prochaine : lundi 18 février à LyonJS, et mardi 19 février au Marseille JUG. Bien sûr il existe maintenant des outils pour faire des présentations en HTML avec plein d'effets spéciaux impressionnants. Mais je dois être de la vieille école, parce que j'ai toujours l'impression que ces effets qui en mettent plein la vue détournent l'attention du contenu. En fait je ne mets jamais d'animations dans mes présentations ; sans pousser aussi loin le minimalisme, je penche plus vers la “méthode Lessig”. Donc tout ce dont j'avais besoin c'était d'un outil comme ShowOff, permettant d'écrire rapidement les styles dans une syntaxe markdown.

Mais ShowOff c'est une application serveur, en Ruby, avec une liste de dépendances presque aussi longue que le bras. Du coup j'ai écrit ce week-end une version simplifiée avec AngularJS, une simple application cliente sans côté serveur. Je l'ai mise sur GitHub : angular-showoff. Il y a un lien vers la démo dans le descriptif.


Utilisation d'un $watch

Je peux vous montrer quelques trucs intéressants dans le code. D'abord comme c'est une application AngularJS, on peut modifier le code source markdown des slides (appuyez sur 's' dans la démo), et l'affichage est rafraîchi automatiquement. Ça ne sert à rien à l'usage, sauf éventuellement pour rectifier une erreur à la volée pendant la présentation, mais c'est toujours impressionnant !

Ça marche grâce à un $watch, qui permet de parser le code source à chaque fois qu'il est modifié :

    $rootScope.$watch('source.markdown', function () {
        $rootScope.slides = Presentation.parseSource($rootScope.source.markdown);
    });

Il aurait été possible de faire le binding sur une fonction qui parse le code markdown, mais ça n'aurait pas été une bonne idée, car AngularJS l'aurait exécutée sur chaque évènement, comme un changement de slide. Alors qu'avec un $watch et en stockant le résultat dans le scope, ça permet de parser seulement lorsque c'est nécessaire.

Compilation d'un template depuis une propriété

Comme j'avais besoin de cet outil pour présenter des slides sur AngularJS, je voulais pouvoir montrer des exemples fonctionnels directement à l'intérieur des slides, au moins pour des exemples simples. Il y a un “Hello Word” sur la dernière slide de la démo, si vous voulez voir.

Pour ça, il fallait qu'AngularJS compile le contenu des slides, comme avec un <ng-include>. Mais la directive ngInclude ne permet pas de charger le code du template à inclure depuis une propriété du scope. Du coup j'ai utilisé une directive includeContent qui est une copie légèrement modifiée de ngInclude.

J'ai une autre idée pour faire ça, qui pourrait être plus simple, mais il faudra que je fasse quelques essais parce que je ne sais pas si c'est réalisable ; ça sera pour plus tard.

Directive correspondant à un vrai élément HTML

Il restait juste un problème à régler. Je voulais mettre de la coloration syntaxique sur les blocs de code, en utilisant google-code-prettify, lequel a besoin d'une classe “prettyprint” sur les éléments <pre> à colorer, qui n'est évidement pas présente dans le HTML généré par le parser markdown utilisé. Et il faut aussi déclencher une méthode prettyPrint() s'il y a de tels blocs à décorer.

J'ai utilisé une solution toute simple, à laquelle on ne pense pas forcément. Quand on pense aux directives AngularJS, on imagine ajouter de nouveaux attributs ou éléments au HTML. Mais on peut aussi créer une directive qui s'applique à des éléments standards du HTML. Il me suffisait donc de faire une directive nommée “pre”, pour qu'elle soit déclenchée sur tous les éléments <pre> générés par le parser dans les slides, puisque le code HTML des slides est compilé par AngularJS.

Voici le code de cette directive :

module.directive('pre', ['$rootScope', '$timeout', function($rootScope, $timeout) {
    var prettyPrintTriggered = false;
    return {
        restrict: 'E',
        terminal: true,  // Prevent AngularJS compiling code blocks
        compile: function(element, attrs) {
            if (!attrs['class']) {
                attrs.$set('class', 'prettyprint');
            } else if (attrs['class'] && attrs['class'].split(' ')
                                                .indexOf('prettyprint') == -1) {
                attrs.$set('class', attrs['class'] + ' prettyprint');
            }
            return function(scope, element, attrs) {
                if (!prettyPrintTriggered) {
                    prettyPrintTriggered = true;
                    $timeout(function () {
                        prettyPrintTriggered = false;
                        prettyPrint();
                    });
                }
            };
        }

    };
}]);

Elle rajoute systématiquement la classe CSS “prettyprint”, et elle programme avec un $timeout le déclenchement de google-code-prettify, s'il n'a pas déjà été programmé, car s'il y a plusieurs blocs de code dans une slide, il ne faut l'exécuter qu'une fois.

Pouvoir ajouter ainsi un comportement à n'importe quel tag HTML, ce n'est pas forcément d'un usage très classique, mais je suis sûr que dans certains cas ça peut être très utile, dans des cas comme celui-ci où l'on n'a pas la main sur le code HTML.

Dans le fichier des directives, vous trouverez aussi celle qui sert aux évènements clavier, pour changer de slide, afficher l'aide, le footer, etc. Elle n'a rien de très mystérieux.

***

Et pour les lecteurs de Lyon et Marseille, rendez-vous en début de semaine prochaine pour une soirée complète sur AngularJS !

Aucun commentaire:

Enregistrer un commentaire