Sam & Max » angularjs http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 La mort du tuto angular 27 http://sametmax.com/la-mort-du-tuto-angular/ http://sametmax.com/la-mort-du-tuto-angular/#comments Sun, 14 Jun 2015 18:25:29 +0000 http://sametmax.com/?p=16389 J'avais demandé si il y avait encore des gens intéressés par le tuto angularjs que j'avais commencé. En effet les ressources sur Angular sont généralement de mauvaise qualité, et la réponse avait été un gros OUI.]]> J’avais demandé si il y avait encore des gens intéressés par le tuto angularjs que j’avais commencé. En effet les ressources sur Angular sont généralement de mauvaise qualité, et la réponse avait été un gros OUI.

Je m’étais donc mis en tête de continuer. Malgré la mort annoncée du framework. Malgré une V2 qui va tout casser.

Voyez-vous, j’aime Angular. Bien que je pense que pour des grosses apps une approche comme ReactJS + BackboneJS est plus saine, pour une petit app rapide ou du prototypage c’est très productif et pratique.

En plus, je n’aime vraiment pas manquer à mes engagements, et en presque 3 ans de ce blog, je les ai tenu. J’ai toujours publié les articles promis. Toujours répondu aux mails. Parfois avec des mois de retard, certes, mais je n’ai signé aucun contrat moral sur les deadlines :)

Je vais néanmoins faire une exception ici et envoyer le tuto Angular au cimetière. Toute mes excuses à ceux qui l’attendaient.

Bien sûr il y a le fait que j’ai beaucoup de travail. Cependant si c’était la cause principale je n’écrirais plus ici car ça me demande des heures et des heures chaque semaine.

C’est surtout qu’il se trouve que j’avorte régulièrement des tentatives de retravailler dessus. C’est un signe de manque de motivation certain, et je viens d’en comprendre aujourd’hui la raison : c’est du Javascript.

Jusqu’ici le blog s’était autorisé régulièrement des sorties de la ligne éditoriale, mais un tel travail, un guide complet sur Angular, enterine le JS comme un sujet majeur du site.

Or ce n’est pas le cas.

Je n’aime pas Javascript.

Du coup non seulement me motiver à faire le dossier me demande une énorme énergie (qui pour le moment se transforme en procrastination coupable), mais en prime je me dis que c’est dépenser des ressources pour faire le taff d’une communauté qu’au final je ne supporte que par obligation professionnelle.

Le résultat est donc que bien que je vais continuer à parler de JS et à coder en JS (programmation Web oblige), je ne vais pas lui accorder autant de place sur S&M ou dans mes heures de loisir.

Je remercie ceux qui prennent le temps de le faire, après tout je profite de leur travail. De mon côté, je vais me concentrer sur les domaines qui me sont plus chers, et parler d’autre chose uniquement de manière ponctuelle. L’investissement me parait plus judicieux.

]]>
http://sametmax.com/la-mort-du-tuto-angular/feed/ 27
Je me te tâte à arrêter le tuto Angular 35 http://sametmax.com/je-me-te-tate-a-arreter-le-tuto-angular/ http://sametmax.com/je-me-te-tate-a-arreter-le-tuto-angular/#comments Thu, 15 Jan 2015 04:25:58 +0000 http://sametmax.com/?p=15736 critiques, j'aime beaucoup AngularJS. Mais à l'annonce de la version 2 d'Angular complètement incompatible avec la version 1, seulement quelques années après sa sorties, des questions se sont posées sur l'avenir du framework.]]> Malgré les critiques, j’aime beaucoup AngularJS. Mais à l’annonce de la version 2 d’Angular complètement incompatible avec la version 1, seulement quelques années après sa sortie, des questions se sont posées sur l’avenir du framework.

J’utilise AngularJS pour certains de mes projets, je suis payé pour former sur AngularJS et j’ai commencé à écrire un guide sur le sujet.

Mais je me demande franchement si ça vaut le coup de s’investir plus.

Je vais continuer à l’utiliser pour mes projets actuels, tout en regardant du côté de la concurrence ici et .

Si j’écris l’article, ce n’est pas pour vous alarmer. Ni même pour vous dire “arrêtez d’utiliser Angular”. Je pense que la V1 va être maintenue par la communauté pendant des années.

Non, je me demande simplement si ça vaut le coup que je me casse le cul à finir le guide.

J’ai du travail à la pelle, le guide sur les tests en Python, Django une app à la fois, 0bin à porter en Python 3…

Bref, est-ce que vous êtes toujours aussi intéressés par ce guide ?

]]>
http://sametmax.com/je-me-te-tate-a-arreter-le-tuto-angular/feed/ 35
AngularJS pour les utilisateurs de jQuery : partie 2 6 http://sametmax.com/angularjs-pour-les-utilisateurs-de-jquery-partie-2/ http://sametmax.com/angularjs-pour-les-utilisateurs-de-jquery-partie-2/#comments Mon, 27 Oct 2014 06:53:27 +0000 http://sametmax.com/?p=12561 Après une intro sans trop de code, on va passer à du concret.

Pour apprivoiser la bête, on va comme d’habitude lui faire donner la patte et dire bonjour :

<!doctype html>
<!-- Lien entre l'app et la page -->
<html ng-app="tuto">
  <head>
    <meta charset="utf-8" />
    <title>Hello Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
    <script type="text/javascript">
 
    // Déclaration de l'app
    var app = angular.module('tuto', [])
 
    // Lancement du code quand angular est prêt
    app.run(function(){
        alert('Hello !')
    })
 
    </script>
  </head>
  <body>
  </body>
</html>

Pour comprendre ce snippet, il faut réaliser qu’Angular est d’abord et avant tout un moyen d’organiser son code, et qu’il vous incite à le séparer en conteneurs :

  • Des modules, qu’on appelle aussi apps. Ce sont les boîtes qui contiennent les autres boîtes. L’équivalent des apps Django, en fait : on peut mettre tout le programme dedans, mais on peut aussi en faire une lib réutilisable.
  • Les providers : des fournisseurs de services. Tout le code métier est dedans.
  • Les directives : tout le code de manipulation du DOM est dedans.
  • Les contrôleurs : fait le lien entre le HTML et les données.

Dans ce code, nous déclarons un module (ou app, c’est kif-kif) nommé “tuto” et n’ayant aucune dépendance (le second paramètre est un array vide) :

var app = angular.module('tuto', [])

Les apps ne font pas grand chose. Ce sont de gros namespaces avec des dépendances, c’est à dire qui déclarent les autres apps nécessaires à leur fonctionnement. Par défaut, aucune autre app n’est obligatoire pour lancer notre code, donc on ne déclare aucune dépendance.

Il y a juste un piège qu’il faut connaître. Si je fais :

var app = angular.module('tuto')

(notez l’absence de second paramètre)

Angular va tenter de me chercher la référence d’app du nom de “tuto” qui existe déjà. L’absence de dépendance se note donc avec un array vide, et non l’absence d’array. Subtilité piégeuse s’il en est. Vu qu’on n’aura pas besoin de déclarer de dépendances avant longtemps, inutile de vous crisper dessus, je vous l’indique juste pour le débuggage qui va immanquablement arriver le jour où vous oublierez l’array vide.

Ensuite, on fait le lien entre notre code HTML et l’app via :

<html ng-app="tuto">

ng-app est ce qu’on appelle une directive. Toutes les directives préfixées ng- sont fournies avec le framework et déjà chargées, prêtes à être utilisées. Une directive est un bout de code Javascript appliqué à du HTML, dans ce cas précis via un attribut.

Vous vous souvenez dans les années 2000 comme on vous faisait chier avec le fait de ne pas mettre de Javascript inline dans du HTML ? Les directives, c’est du Javascript inline qui ne dit pas son nom.

Chaque page doit être liée à une app, et une seule. Si on ne met pas ng-app, notre code ne se chargera pas, si on en met 2, ça foire. C’est le point d’entrée de notre code. Nous déclarons donc que cette page sera gérée par notre app “tuto”.

Puis lance notre hello tonitruant :

    app.run(function(){
        alert('Hello !')
    })

app est la variable contenant la référence à notre app, et en utilisant la méthode .run, on lui passe un callback à appeler quand l’app sera prête.

Il y a plusieurs choses à réaliser ici :

  • C’est un callback, donc le code de la fonction ne s’exécute pas tout de suite. Il sera lancé après l’initialisation de l’app.
  • C’est ce qu’il y a de plus proche du $.ready de jQuery.
  • On n’utilise presque jamais ce code avec Angular.

Vous allez me dire, mais espèce de pignouf, pourquoi tu nous montres un code que personne n’utilise ?

Et bien parce que le titre du dossier est AngularJS pour les utilisateurs de jQuery, du coup je vous mets en terrain connu.

Mais la vérité, c’est qu’un vrai hello d’Angular ressemble à ça :

<!doctype html>
<html ng-app="tuto">
  <head>
    <meta charset="utf-8" />
    <title>Hello Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
    <script type="text/javascript">
 
    /* La je recréé l'app à chaque fois pour des raisons de praticité, mais
        dans un vrai code vous auriez ça dans un fichier à part, une seule
        fois */
    var app = angular.module('tuto', [])
 
    // Création d'un controller, et attachement d'une variable au scope.
    app.controller('HelloCtrl', function($scope){
        // Attachement d'une donnée au scope
        $scope.hello = "Hello"
    })
 
    </script>
  </head>
  <body ng-controller="HelloCtrl">
    <!-- Affichage du message attaché au scope -->
    <h1>{{ hello }}</h1>
  </body>
</html>

Et là il y a beaucoup à dire.

Premièrement, contrairement à jQuery, il n’y a pas de notion de préparation du DOM. En vérité, on peut mettre son code Angular en vrac, et Angular va initialiser tout son monde dans le bon ordre, et exécuter ce qu’il faut au bon moment. Et ce bon moment est parfois avant que le DOM soit prêt.

Mais vous n’avez pas à vous en soucier.

Car avec Angular, on manipule très rarement le DOM. Très peu de $('selecteur'), $node.bind() et autre $node.append(), à part dans les directives.

La majorité de votre taf va être de définir des données en pur Javascript, de dire où elles vont dans la page, et de les manipuler en Javascript pendant qu’Angular s’occupe de mettre la page à jour automatiquement.

Nous avons pas mal de nouvelles notions ici. D’abord, le contrôleur…

C’est un bout de code qu’on va lier à un bout de HTML via la directive ng-controller :

  <body ng-controller="HelloCtrl">
    ...
  </body>

Le contrôleur va être responsable de mettre à disposition des données pour ce bout de HTML. On peut lier autant de contrôleurs qu’on veut dans une page, et même les imbriquer. Ici, on dit que notre contrôleur HelloCtrl est responsable de mettre des données à disposition pour le tag body et tout ce qu’il contient.

On fait rarement des contrôleurs qui ratissent aussi large dans de vraies apps, mais pour notre exercice, c’est très bien.

Comment met-on à disposition des données ? Qu’est-ce que veut dire “mettre à disposition” ? Quelles données ?

Notre contrôleur ressemble à ceci :

    app.controller('HelloCtrl', function($scope){
        $scope.hello = "Hello"
    })

La fonction de callback sera exécutée quand ng-controller="HelloCtrl" sera activé automatiquement par Angular. Vous n’avez pas à vous souciez de quand, il le fera au moment le plus optimisé.

La seule chose dont vous avez à vous soucier, c’est ce que vous allez mettre dedans.

Et il y a ici deux nouvelles notions :

  • L’injection de dépendances.
  • Le service (je vous explique ce qu’est un service plus loin) $scope.

L’injection de dépendance fait partie de ces trucs qu’Angular fait automatiquement pour vous. Quand vous déclarez une variable dans le callback d’un contrôleur, Angular va chercher ce nom en mémoire, et récupérer le service qui porte ce nom. S’il ne le trouve pas, il plante.

Une fois qu’il a trouvé le service, il va attendre tranquillement le bon moment pour appeler le callback. Ce moment opportun arrivant, il va lui passer le service en paramètre. On dit qu’il lui “injecte” le service.

En relisant ces paragraphes, je réalise qu’il y a un mélange de plein de termes : contrôleurs, services, injection, bla, bla, bla.

Voilà le deal :

  1. Vous déclarez un contrôleur et vous le liez à du HTML via ng-controller.
  2. Dans ce contrôleur vous dites “j’ai besoin du service Machin”
  3. Angular cherche s’il connaît Machin. Si non, il plante.
  4. Angular prépare le HTML, décide qu’il est prêt, appelle votre contrôleur et lui passe Machin en paramètre automatiquement.

Les services sont justes des d’objets javascript ordinaires, qu’on a nommés d’une certaine manière pour qu’Angular puisse les injecter. C’est tout.

En effet, pour résoudre le problèmes d’espace de nom en Javascript et d’organisation d’une grosse application, les créateurs d’Angular on créé un grand registre. On enregistre notre code dedans sous un nom (Angular.module('nom_de_l_app'), app.controller('nom_du_controller'), etc.), et Angular récupère le bon code au bon moment. C’est essentiellement un moyen de pallier au fait que Javascript est dégueulasse.

Donc, plutôt que de faire des imports comme en Python, on va déclarer une variable en paramètre, et Angular injecte le bon objet pour vous. C’est bizarre, mais on s’habitue.

Du coup, là :

app.controller('HelloCtrl', function($scope){

Je dis “crée moi le contrôleur nommé ‘HelloCtrl’, et quand tu vas appeler le callback, passe lui l’objet nommé ‘$scope’ en paramètre. Fais ça au bon moment, je m’en branle, je veux pas le savoir. Démmerde toi, on a tous des problèmes.”

Le résultat, c’est que votre fonction sera exécutée automatiquement au bon moment, et qu’elle aura $scope qui lui sera passé.

Alors à quoi sert ce $scope ?

C’est simple : tout ce qui est attaché en attribut de $scope est accessible dans le HTML géré par ce contrôleur. Quand je fais :

    app.controller('HelloCtrl', function($scope){
        $scope.hello = "Hello"
    })

Et que après je lie mon contrôleur au HTML :

  <body ng-controller="HelloCtrl">
    <!-- Affichage du message attaché au scope -->
    <h1>{{ hello }}</h1>
  </body>

Ma variable hello est disponible ici (via les petites moustaches qu’on verra au prochain chapitre), parce que je suis dans body qui est géré par le contrôleur HelloCtrl qui contient un $scope avec l’attribut hello. Fiou ! Ca en fait des couches. Mais Angular, c’est comme un orgre un onion.

Vous touchez ici du doigt l’essence même du boulot des contrôleurs, et à peu près leur unique but : mettre des données à disposition du HTML. C’est le C de MVC.

C’est pour cette raison que je vous ai répété “Angular va tout faire au bon moment, vous n’avez pas à vous soucier de quand”. Ceci est en effet très frustrant à entendre quand on vient du monde de jQuery puisque qu’on doit savoir exactement quand on fait les choses : il faut que le DOM soit prêt, il faut que les éléments sur lesquels on travaille existent, il faut qu’on puisse récupérer des nodes et en ajouter.

Avec Angular, on ne fait rien de tout ça. On va déclarer ses données dans le contrôleur, et dire où elles doivent être mises dans la page. Angular se charge de faire le lien pour vous quand le HTML est prêt, quand le Javascript est chargé, quand les Dieux sont avec vous…



Télécharger le code de l’article.

]]>
http://sametmax.com/angularjs-pour-les-utilisateurs-de-jquery-partie-2/feed/ 6
AngularJS pour les utilisateurs de jQuery : partie 1 31 http://sametmax.com/angularjs-pour-les-utilisateurs-de-jquery-partie-1/ http://sametmax.com/angularjs-pour-les-utilisateurs-de-jquery-partie-1/#comments Tue, 02 Sep 2014 09:55:36 +0000 http://sametmax.com/?p=12170 Après avoir sondé un peu Twitter, le sujet intéresse, et il est très, mais alors, très mal traité ailleurs sur le net. Malgré mon aversion, bien connue, pour javascript, je m’en bouffe beaucoup, prog Web oblige.

Angular rend l’utilisation de JS plus supportable, mais possède une très grosse courbe d’apprentissage bien pentue, avec des ressources vraiment mal foutues.

Et surtout, le problème vient de l’habitude de jQuery.

AngularJS est l’anti-jQuery, ça ne fonctionne pas du tout pareil, mais personne n’est là pour vous montrer comment faire la transition. Ce sera le but de ce dossier, un dossier qui va être long car ce framework est un gros morceau.

Quand utiliser AngularJS ?

Tous les sites ne sont pas faits pour être créés avec Angular. En effet, si votre site est essentiellement composé de contenu statique, avec juste un peu de dynamique pour quelques widgets, Angular n’a aucun intérêt.

Angular est lourd à mettre en œuvre, on va donc se faire chier à sortir le bazooka quand on monte au front, pas quand on fait une garde de nuit en rase campagne.

Malgré le fait que Google interprète maintenant les pages en JS, le texte reste roi, et Angular vous force à faire des pages en pur JS, rendant le référencement aléatoire de votre contenu. Les hacks que l’on voit un peu partout sur la toile pour compenser cela sont très loin d’obtenir l’effet d’un référencement naturel de contenu facilement accessible à l’ancienne.

En fait, le plus gros défaut d’Angular, c’est qu’il ne permet pas, pour le moment, de faire de la dégradation gracieuse. Soit le mec a Javascript et il a accès au site. Soit le site est illisible.

jQuery a donc encore de beaux jours devant lui.

Typiquement, on ne prendra pas Angular pour :

  • La partie publique d’un blog de type WordPress.
  • Un wiki type Wikipédia.
  • Un forum ou site collaboratif.
  • Un tube vidéo.

Ce sont des sites dont le contenu est important. On veut que les moteurs de recherche puisse les indexer de fond en comble. On veut que ce soit lisible sans JS et par les aveugles (sauf le tube vidéo :-D). Les ajouts de JS se feront par petites touches.

On utilisera plutôt Angular pour :

  • Une app grand public.
  • La partie admin d’un blog.
  • Un logiciel d’entreprise.
  • Un réseau social.

Ce sont des sites pour lesquels l’ergonomie est plus importante que le contenu. Le public est captif, et on peut exiger Javascript, prix à payer pour une expérience utilisateur moderne.

Vous noterez qu’il y a une certaine opposition entre vieux format (wiki, forum, blog) et format contemporain (app, réseau social). Ce n’est pas un hasard. Les premiers sont orientés contenu, les seconds orientés usage. C’est ainsi que le Web a évolué, et Angular est là exactement pour répondre à cette évolution.

Il est néanmoins tout à fait possible de faire des choses hybrides. Rien n’empêche de faire des parties du sites statiques, et d’autres très dynamiques, de mélanger les deux sur une même page. Par exemple, sur un blog, vous pouvez mettre l’article en statique pour le ref et la facilité de consultation, et la partie commentaires en Angular, pour améliorer l’expérience utilisateur.

Que va apporter Angular ?

Les avantages sont doubles.

D’abords, il y a les avantages pour vous, le développeur. Angular vous force en effet à utiliser un style Javascript très propre qui tend à découpler les composants, isoler les services, lisser les angles et refaire le papier peint à fleur. L’application en devient plus propre, plus facile à faire évoluer et à maintenir. Toutes les bonnes pratiques d’organisation d’une app JS ont une réponse angularesque. Ce n’est pas forcément celle que vous voulez, mais elle marche.

Ensuite, Angular va vous rendre plus productif. Pas tout de suite, évidement, coincés que vous êtes dans votre logique jQuerinne. Mais une fois le cap passé, coder beaucoup de JS sera immensément plus simple et plus rapide, car Angular vous épargne beaucoup de logique répétitive.

En prime, ce framework est orienté tests. Ce n’est pas obligatoire, mais c’est bien plus facile qu’avec des modules jQuery.

Mais il y a également les avantages pour l’utilisateur. Puisque qu’Angular est complètement dynamique, il bénéficiera automatiquement d’une app plus réactive, avec moins de rechargements, plus de fonctionnalités aussi. En effet, vous rechignerez moins à rajouter cette fonction “delete-row” si vous savez que ça vous prend 3 lignes de code.

Attention cependant, il y a aussi des inconvénients.

Angular est lourd, c’est long à charger, ça bouffe de la ressource pour chaque tab, et si vous avez beaucoup de traitements sur une page, vous avez intérêt à optimiser votre race le bouzin sinon ça va ramer. Tout le monde n’est pas sur une machine de dev propre à 8 cœurs avec une ligne ADSL qui a tout compris.

En fait, si votre site est destiné à être consulté ainsi : je cherche, je consulte du contenu, et je m’en vais, charger Angular est un pur gâchis. Il faut au moins que l’on interagisse un peu avec le site pour que ça vaille le coup : créer du contenu, manipuler des informations, se faire notifier, etc.

Et puis il y a la tentation, la tentation forte de tout faire à la main côté client. Avec son lot de problèmes de sécurité (le delete qui fait pas de vérif), de merdier dans l’histo de navigation (le clic du milieu qui marche pas, le back qui back pas où on veut), les sessions qui font des siennes (je suis déco, mais y mon avartar là, pourquoi ?), etc.

Angular n’est pas une solution pour le bidouilleur Javascript de devenir soudainement grand programmeur Web, et ne dispense pas de coder correctement.

Si je fais de l’Angular, je dois faire du NodeJS ?

Non.

Angular est une techno parfaitement neutre, elle est limitée au client, et par conséquent, vous êtes libres de choisir votre techno côté serveur : .Net, PHP, Ruby, Java, Python…

Par contre, pour les tests, les outils sont très orientés autour de l’écosystème NodeJS : npm, grunt, PhantomJS, etc. Il va falloir mettre les mains dans le cambouis, avec son lot de trucs qui s’installent pas, de dépendances mal branlées, de doc pas à jour, de code JS peu expressif et tout le bordel. C’est tout le problème du JS, la communauté a gardé le côté crade et à l’arrache du langage. Heureusement, ils ont récupéré les designers du monde Ruby, donc vous aurez des tutos obsolètes, mais très jolis.

Ok, mais je peux faire quoi avec ?

Car finalement, quand on n’a pas utilisé Angular, on se demande finalement qu’est-ce que ça apporte par rapport à jQuery.

Essentiellement, c’est que ça divise par 100 les manipulations du DOM. Avec Angular, on met ses données dans un tableau d’objets JS, on indique où les afficher, et on manipule ensuite notre array. Le DOM est mis à jour sans qu’on doive s’en soucier.

Ça à l’air de rien comme ça, mais si vous ouvrez vos fichiers JS avec jQuery, vous verrez qu’ils sont blindés de .click()code et de .append(). C’est la majorité du code ! Code qu’on a pas besoin d’écrire avec Angular.

L’organisation d’Angular invite aussi naturellement à écrire un code linéaire. Pas de blocs dans des blocs dans des blocs, chaînes de callbacks et amas de closures. Il y en a toujours, mais bien moins.

Après, on fait finalement la même chose qu’on ferait avec jQuery, mais plus facilement. Ce qui explique qu’on fait rarement une app full JS avec jQuery, ce qui est infernal à coder, alors qu’on le fait systématiquement avec Angular, car c’est son milieu naturel.

Ce qui va vous surprendre avec cette façon de faire, c’est que tout ce qui s’affiche dans la page passe par Angular. En fait, on a généralement une API REST, et Angular en front pour afficher le site. Pas trop de templating côté serveur.

Et on verra des exemples concrets de tout ça dans les parties suivantes :)

]]>
http://sametmax.com/angularjs-pour-les-utilisateurs-de-jquery-partie-1/feed/ 31
Rendre un élément scrollable avec Angular 8 http://sametmax.com/rendre-un-element-scrollable-avec-angular/ http://sametmax.com/rendre-un-element-scrollable-avec-angular/#comments Tue, 01 Jul 2014 09:30:20 +0000 http://sametmax.com/?p=11240 Petit snippet que j’utilise dans mes apps angular. Ça permet de définir un comportement quand l’utilisateur scrolle au-dessus d’un élément. Typiquement, augmenter la valeur d’un champ, faire défiler un carousel, etc. Il faut, bien entendu, éviter que la page scrolle elle-même.

Implémentation

app.directive('wheelable', function() {
"use strict";
 
  /* On définit sur quels attributs on va mettre les callbacks */
  var directive = {
      scope: {
          'onWheelUp': '&onwheelup',
          'onWheelDown': '&onwheeldown'
      }
  };
 
  /* On limite la directive aux attributs */
  directive.restrict = 'A';
 
  /* Le code qu'active la directive quand on la pose sur l'élément */
  directive.link = function($scope, element, attributes) {
 
      /* On attache un callback à tous les événements de scrolling */
      element.bind('mousewheel wheel', function(e) {
 
        /* On vérifie si l'utilisateur scroll up ou down */
        if (e.originalEvent) {
          e = e.originalEvent;
        }
        var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
        var isScrollingUp = (e.detail || delta > 0);
 
        /* On appelle le bon callback utilisateur */
        if (isScrollingUp){
          $scope.$apply($scope.onWheelUp());
        } else {
          $scope.$apply($scope.onWheelDown());
        }
 
        /* On évite que la page scrolle */
        e.preventDefault();
      });
  };
 
  return directive;
});

Usage

Comme pour toutes les directives qui impliquent des callbacks, il faut définir des fonctions et les attacher à votre scope dans un controleur (ou un service attaché au controleur) :

app.controller('FooCtrl', function($scope) {
"use strict";
  $scope.votreCallBackPourQuandCaScrollDown = function(){
    // faire un truc par exemple moi je l'utilise pour changer
    // la valeur de l'élément.
  };
  $scope.votreCallBackPourQuandCaScrollDown = function(){
    // faire un autre truc
  };
});

La directive s’utilise en mettant l’attribut wheelable sur l’élément qu’on veut rendre scrollable. Ensuite on déclare dans les attributs onwheeldown et onwheelup le code à exécuter, et zou :

<div ng-controller="FooCtrl">
  ...
  <input type="text" wheelable
         onwheeldown="votreCallBackPourQuandCaScrollDown()"
         onwheelup="votreCallBackPourQuandCaScrollUp()"
         >
  ...
</div>
]]>
http://sametmax.com/rendre-un-element-scrollable-avec-angular/feed/ 8
La protection CSRF de Django et les requêtes Ajax 19 http://sametmax.com/la-protection-csrf-de-django-et-les-requetes-ajax/ http://sametmax.com/la-protection-csrf-de-django-et-les-requetes-ajax/#comments Tue, 10 Jun 2014 04:18:36 +0000 http://sametmax.com/?p=10438 attaques CSRF est dans le top 10 des erreurs les plus chiantes en Django, main dans la main avec les fichiers statiques qui ne marchent pas, les URL qui ne matchent pas et les CBV qui nheuuuu, juste pas.]]> La protection contre les attaques CSRF est dans le top 10 des erreurs les plus chiantes en Django, main dans la main avec les fichiers statiques qui ne marchent pas, les URL qui ne matchent pas et les CBV qui nheuuuu, juste pas.

Une fois qu’on a compris le principe, ça va pour la prog normal, mais un jour on a besoin de faire un POST en Ajax, et on se retrouve avec une erreur invisible. Après avoir dégainé Firebug, on comprend qu’on a une 403 forbidden, et votre cerveau finit (la durée galérienne est plus ou moins longue selon les profiles, les heures de sommeil et les phases de la lune) par réaliser qu’on n’a pas envoyé le token CSRF. Merde.

C’est là que généralement les gens sortent du @csrf_exempt, ou carrément, en finissent avec cette solution radicale :

MIDDLEWARE_CLASSES = (
    'django.middleware.gzip.GZipMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
)

Mais c’est quand même dommage d’en arriver là alors qu’on peut le faire proprement.

D’abord, le token est sauvegardé dans un cookie. Il faut donc le récupérer.

// ue petite fonction pour récupérer la valeur d'un cookie,
// puisque bien entendu, comme toutes les APIS javascripts,
// les choses qu'on fait le plus souvent ne sont pas incluses
// en natif. Oui je suis aigri.
function getCookie(name) {
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                return decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
}

Sinon, pour les fainéants, il y a y a un plugin jquery qui permet de faire $.cookie('nom').

Ensuite, on attache cette valeur en header avec toutes les requêtes POST. Comme ça, plus besoin de l’inclure manuellement, on peut faire ses requêtes sans y penser.

Avec jQuery :

$.ajaxSetup({
    // fonction appelée avant d'envoyer une requête AJAX
    beforeSend: function(xhr, settings) {
         // on ajoute le header que si la requête est pour le site en cours
         // (URL relative) et est de type POST
         if (!/^https?:.*/.test(settings.url)  && settings.type == "POST") {
             // attachement du token dans le header
             xhr.setRequestHeader("X-CSRFToken",  getCookie('csrftoken'));
         }
     }
});

Avec AngularJs :

// interception de la configuration du provider HTTP
// qui possède un mécanisme déjà tout prêt pour ça
votre_app.config(function($httpProvider) {
    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});

Attention, si l’app n’est pas servie par Django (template statique avec uniquement des appels Ajax), il faut faire au moins un GET avant de faire son premier POST afin d’obtenir le cookie.

]]>
http://sametmax.com/la-protection-csrf-de-django-et-les-requetes-ajax/feed/ 19
Service, factory et provider dans AngularJS 28 http://sametmax.com/service-factory-et-provider-dans-angularjs/ http://sametmax.com/service-factory-et-provider-dans-angularjs/#comments Tue, 29 Apr 2014 09:07:41 +0000 http://sametmax.com/?p=10095 AngularJS est un framework difficile à prendre en main. Pas parce qu’il est particulièrement compliqué, mais parce que ses concepts sont vraiment différents de ceux qu’on a l’habitude de rencontrer dans les frameworks habituels. Le pire, c’est quand on vient de jQuery, car Angular est un peu l’anti-jQuery et il faut littéralement désapprendre ses habitudes.

Généralement, les gens s’en sortent avec les contrôleurs. Ils ne mettent pas le bon code dedans, ils ne savent pas comment rendre les bouts de code indépendants et réutilisables, mais ils arrivent à en faire quelque chose. Les directives, ils n’y touchent pas, mais ils peuvent s’en passer pendant un certain temps et juste réutiliser du code trouvé sur Github.

Par contre le côté service/factory/provider, ça c’est un gros problème. On ne peut pas faire sans, mais peu de gens savent faire avec. En codant All that counts, j’ai réalisé que c’était un bon tuto à faire. Donc en avant.

Article long = musique, évidement.

On m’a très justement demandé les prérequis pour suivre cet article : il faut avoir fait des tutos de base sur Angular et notamment comprendre l’injection de dépendance et le data binding.

Que font ces trucs là ?

Techniquement, un service, une factory ou un provider, dans AngularJS, ça sert à la même chose. Un service est juste une manière plus facile d’écrire une factory, qui est juste une manière plus simple d’écrire un provider.

Les 3 servent à créer un objet Javascript ordinaire, c’est tout.

Oui, oui, c’est vraiment tout. C’est le seul et unique but de cela. Vous allez me dire, mais alors pourquoi se faire chier à les utiliser alors qu’on peut écrire un objet à la main ? Tout simplement parce qu’ils permettent d’encapsuler cette création, l’isoler du reste du code, un problème toujours difficile en Javascript.

Ils sont là pour éviter de pourrir le namespace global, tout en n’utilisant pas la technique bien connu de “je met un gros conteneur avec tout dedans et tout le monde y accède”, qu’on a tendance à voir dans la plupart des projets JS. Le code est ainsi plus maintenable et testable.

Chaque service/factory/provider permet d’avoir un groupe de fonctionnalités séparé du reste. On peut utiliser celui-ci facilement partout ailleurs en utilisant l’injection de dépendance. Si vous ne savez pas ce que c’est, arrêtez-vous tout de suite, c’est le principe de fonctionnement de base d’Angular, il faut quitter cet article et prendre un tuto pour débutant. Revenez ensuite.

Dans un service/factory/provider, on va donc mettre du code métier et/ou des données de l’app, mais groupés par thème.

Par exemple, sur une app video, on peut en faire un qui va contenir le code pour gérer le profile utilisateur et un pour gérer la playlist.

On aura donc deux services/factories/providers, qui vont créer au final deux objets. Chaque objet va contenir des méthodes et des données. Pour le profile, on peut imaginer qu’il va contenir le nom d’utilisateur, son ID, et une méthode de login et de logout :

{
    "name": "Hodor",
    "Id": 7898,
    "login": function(){
        // faire un truc pour se loger
    },
    "logout": function(){
        // faire un truc pour se deco
    }
}

Pour la playlist, on peut imaginer que ça va contenir la liste des videos, la video en cours de lecture et de quoi passer à la suivante et précédente :

{
    "name" : "Super playlist"
    "videos": [
        {"title": "video 1", "url": "http://..."},
        {"title": "video 2", "url": "http://..."}
    ],
    "currentVideo": 1,
    "next": function(){
        // passer à la vid suivante
    },
    "previous": function(){
        // passer à la vid précédente
    }
 
}

Et c’est tout. Ce n’est ni plus ni moins que ça, des objets normaux dans lesquels ont va mettre nos données et nos méthodes. Ce qui est “special”, c’est comment ils sont créés et utilisés. Et c’est ça qu’on va voir tout de suite.

Créer un objet via un service/factory/provider

Voyons le plus compliqué d’abord : le provider. Les deux autres ne sont que des raccourcis pour écrire un provider, de toute façon.

Le rôle du provider, c’est de créer et mettre à disposition un objet. C’est tout. Il n’y a rien de plus. Je répète, ça ne sert qu’à ça. “provider”, ça veut dire “fournisseur” en anglais. Et c’est ce que ça fait : ça fournit un objet. Supposons que vous avez déjà créé une app “monApp”. Écrire un provider pour créer l’objet “profile” ressemble à ça :

// On appelle notre objet qui sera créé "profile". Ce qui signifie que l'on
// pourra ensuite utiliser le nom "profile" pour annoncer quand
// on souhaite utiliser l'objet issu de ce provider.
monApp.provider("profile", function(){
 
    // Un provider, au final, c'est juste une fonction anonyme qu'on relie
    // à un nom :)
 
    // Cette fonction DOIT avoir une méthode nommé "$get" attachée à son "this"
    this.$get = function() {
 
        // Et la méthode "$get" DOIT retourner l'objet qu'on veut créer.
        return {
            "name": "Anonymous",
            "Id": null,
            "login": function(){
                // faire un truc pour se loger
            },
            "logout": function(){
                // faire un truc pour se deco
            }
        }
    }
});

C’est tout. Je vous jure que c’est tout. C’est juste une boîte qu’Angular va prendre, et appeler la méthode $get pour obtenir un objet. En plus, un seul objet, car la méthode ne sera appelée qu’une fois : l’objet sera un singleton.

Plus tard dans le code, quand vous ferez un controller :

mymonAppApp.controller('UnControlleurCommeUnAutre', function($scope, profile) {
    $scope.profile = profile;
});

Vous utiliserez profile en paramètre, il va donc être injecté automatiquement. C’est tout le principe d’angular, des tas de boîtes qui déclarent quelles autres boîtes elles utilisent via l’injection de dépendance.

Ici, l’objet va être créé (si ce n’est pas déjà fait ailleurs), et c’est tout. Le boulot d’un controller, c’est essentiellement de mettre des objets issus des providers dans le scope pour que le HTML puisse les utiliser. Je vous promet, c’est 90% des use cases.

Votre objet sera le conteneur que l’on va manipuler pour changer le nom du profile, se logger, etc. C’est juste une boîte qui rend un service. D’ailleurs on appelle souvent les objets issus des providers des “services”.

Utiliser des providers évite de créer tout une logique de classes, de gérer des namespaces, des instanciations : on est obligé de faire de l’encapsulation et de la composition. Ca rend le code (javascript) plus propre et plus testable, bien décomposé et découplé. Avoir un code propre est le but principal des providers, ils ne fournissent rien comme fonctionnalités qu’on ne puisse faire autrement.

Maintenant voyons ce qu’est une factory. En fait, c’est juste un raccourci pour écrire un provider :

monApp.factory('profile', function() {
    return {
        "name": "Anonymous",
        "Id": null,
        "login": function(){
            // faire un truc pour se loger
        },
        "logout": function(){
            // faire un truc pour se deco
        }
    }
});

Ça fait exactement la même chose, et on l’utilise exactement pareil. C’est juste plus court. Pas de méthode $get à écrire, on retourne l’objet cash pistache.

Pourquoi on utilise un provider alors ? Parce que c’est plus pratique à configurer, comme on le verra plus bas. Mais la factory est suffisante la plupart du temps. Ce qui est marrant, c’est qu’en terme informatique, un provider est un design pattern “factory”, ce qui m’a vachement rendu confus au début…

Et le service alors ? C’est une syntaxe alternative :

monApp.service('profile', function() {
    this.name = "Anonymous";
    this.id = null;
    this.login = function(){
        // faire un truc pour se loger
    }
    this.logout = function(){
        // faire un truc pour se deco
    }
});

La fonction va être utilisée directement (avec new) pour créer l’objet, donc pas besoin de return : on attache tout à this. Mais le résultat est strictement le même. Le choix entre une factory et un service est vraiment une question de goût. Personnellement je vous conseille d’utiliser des factories car ça vous évite le casse-tête de la portée de this, grande cause d’erreurs en JS.

Dépendances et injections

Souvenez-vous qu’on peut faire ça :

mymonAppApp.controller('UnControlleurCommeUnAutre', function($scope, profile) {
    $scope.profile = profile;
});

Pour dire “mon controller dépend de profile, donc s’il te plait Angular, crée l’objet et passe le moi”.

Et bien ça marche aussi entre les providers/factories/services. Par exemple, si je fais un provider pour ma playlist, mais que la playlist dépend du profile :

monApp.provider("playlist", function(){
 
    // En utilisant le nom de l'autre provider ici, je demande à ce qu'il
    // soit injecté ici, et donc disponible ici. Je dis "ce provider
    // est dépendant de l'objet fournit par cet autre provider."
    this.$get = function(profile) {
 
        return {
            // Et du coup je peux accéder aux attributs de l'objet.
            "name": "Playlist de " + profile.name
            "videos": [],
            "currentVideo": 1,
            "next": function(){
                // passer à la vid suivante
            },
            "previous": function(){
                // passer à la vid précédente
            }
        }
    }
});

Si je le faisais avec une factory, se serait pareil, mais en plus simple :

// l'injection de dépendance se fait au niveau de la fonction anonyme
// car il n'y a pas de méthode "$get"
monApp.factory("playlist", function(profile){
    return {
        "name": "Playlist de " + profile.name
        "videos": [],
        "currentVideo": 1,
        "next": function(){
            // passer à la vid suivante
        },
        "previous": function(){
            // passer à la vid précédente
        }
    }
});

L’interêt des providers

L’interêt principal d’utiliser un provider plutôt qu’une factory, c’est qu’on peut le rendre configurable.

Par exemple, je veux que ma playlist ne soit jamais vide, alors si elle l’en, on met deux vidéos par défaut dedans :

monApp.factory("playlist", function(profile){
 
    // je ne retourne pas l'objet tout de suite, je vais le modifier
    var playlist = {
        "name": "Playlist de " + profile.name
        "videos": [],
        "currentVideo": 1,
        "next": function(){
            // passer à la vid suivante
        },
        "previous": function(){
            // passer à la vid précédente
        }
    };
 
    // hop, je m'assure que la playlist n'est jamais vide en modifiant
    // l'objet
    if (playlist.videos.length === 0){
        playlist.videos = [
            {"title": "video 1", "url": "http://..."},
            {"title": "video 2", "url": "http://..."}
        ]
    }
 
    // ne pas oublier de retourner l'objet à la fin quand même :)
    return playlist;
});

Tout ça sera largement suffisant dans la plupart des cas. Mais dans le rare cas où je veux faire une lib réutilisable, je veux que ces vidéos par défaut soient configurables. Comment alors permettre que l’utilisateur de ma lib les choisisse ?

En utilisant un provider :

monApp.provider("playlist", function(){
 
    // On déclare des données attachée sur le provider. PAS sur l'objet que
    // le provider va créer, attention. Sur le provider lui-même.
    // Le provider, je le rappelle, c'est cette fonction anonyme qui retourne
    // un objet via sa méthode "$get" (oui, les fonctions sont des objets
    // en JS, et peuvent avoir des méthodes. Ce langage est très clair.)
    this.defaultVideos = [
            {"title": "video 1", "url": "http://..."},
            {"title": "video 2", "url": "http://..."}
    ]
 
    // ensuite je fais ma méthode "$get" qui va retourner l'objet voulu,
    // comme d'hab
    this.$get = function(profile) {
 
        var playlist = {
            "name": "Playlist de " + profile.name
            "videos": [],
            "currentVideo": 1,
            "next": function(){
                // passer à la vid suivante
            },
            "previous": function(){
                // passer à la vid précédente
            }
        }
 
        // Au moment de la création de l'objet, j'utilise les données
        // attachées au provider pour fournir la valeur par défaut.
        if (playlist.videos.length === 0){
            playlist.videos = this.defaultVideos;
        }
 
        return playlist;
    }
});

Jusqu’ici, vous allez me dire, mais quelle est la différence avec le précédent ? Et bien il se trouve qu’Angular met à votre disposition automatiquement playlist, mais également playlistProvider, une référence sur le provider qui va créer l’objet playlist.

ATTENTION : quand vous déclarez ici “playlist”, “playlist” est le nom de l’objet créé par le provider. Angular, lui, va automagiquement attacher le nom “playlistProvider” au provider qui a créé l’objet. Vous n’avez rien à faire pour cela, il va automatiquement s’appeler “nomDeVotreObjetProvider” et sera mis à votre disposition.

Du coup :

// On peut utiliser "config" pour lancer du code avant la création des objets
// par les providers. Tout le code dans des blocs "config" est toujours lancé en premier,
// avant tous les providers/factories/services de cette app.
monApp.config(function(playlistProvider){
    // ici l'utilisateur de votre lib a accès au provider avant la création de
    // l'objet, et à donc le loisir de changer les valeurs par défaut
    playlistProvider.defaultVideos = [
            {"title": "Autre video", "url": "http://..."},
    ]
});

Ce n’est pas un cas courant, et la plupart du temps, utiliser une factory marchera très bien.

Et tout ça, je m’en sers pour quoi ?

On met dans les providers/services/factories, tout le code qui n’est pas lié à la navigation ou à la manipulation de DOM. Bref, tout le code de la logique de votre app. Le code qui n’est pas lié au Web. Un profile n’a rien à avoir avec le Web. Une playlist n’a rien à voir avec le Web. Mais il faut un code pour les gérer, et des variables vont devoir êtres mises quelque part.

Généralement, ces providers/services/factories sont utilisés :

  • Par un controller.
  • Par une directive.
  • Par d’autres providers/services/factories.

Mais aussi, directement dans le HTML.

En effet, quand vous allez faire ça :

// le boulot du controller, c'est essentiellement d'attacher les bons
// services au scope, je vous dis !
mymonAppApp.controller('VideoCtrl', function($scope, playlist) {
    $scope.playlist = playlist;
});

Vous allez ensuite pouvoir déclarer votre controller dans votre HTML. Dans un
bloc de HTML couvert par un controller, vous avez accès à toutes les attributs
du scope, donc à votre service “playlist”. Et du coup, paf, à tout son code :

<div ng-controller="VideoCtrl">
    ...
    <p>Playlist :</p>
    <ul>
       <li ng-repeat="vid for playlist.videos">{{ vid.title }}</li>
    </ul>
    <p>
    <button ng-click="playlist.next()">Next</button>
    </p>
</div>

Et c’est ça la beauté d’Angular : tout est bien séparé, et bien rangé. Votre code métier dans les providers, votre code d’interface dans le HTML, votre liaison entre le HTML et les providers à travers les controllers… Enfin du code JS qui ne ressemble pas à des spaghetti trop cuites par un enfant de 6 ans scatoman.

]]>
http://sametmax.com/service-factory-et-provider-dans-angularjs/feed/ 28
Not invented here 19 http://sametmax.com/not-invented-here/ http://sametmax.com/not-invented-here/#comments Thu, 24 Apr 2014 07:18:30 +0000 http://sametmax.com/?p=10065 Il va sur redtube Il réinvente la roue, bien sûr ! ]]> À la maison on joue beaucoup. Aux jeux vidéo, bien entendu, mais aussi à tout un tas d’autres trucs, incluant des jeux de rôles, de société, de carte… Ça joue même de la musique. Il y a un carton qui déborde de cartes magic, l’indispensable pot plein de dés 6/10/20, et des dizaines de boîtes en tout genre, parfois des titres dont moi-même je n’ai jamais entendu parlé, un piano à queue, divers lots de baffles et même des contentions psychiatriques. Pour d’autres jeux.

Je me tâte à faire une rubrique “jeu” d’ailleurs, pour introduire un jeu de plateau de temps en temps.

Évidement, à force de parties, on commence à en avoir plein le cul d’attendre le tour de l’autre, donc on joue au chrono. On a tenté le sablier, la montre, le portable, l’app, et rien n’est vraiment satisfaisant.

Que fait un programmeur dans ce cas ? Il réinvente la roue, bien sûr !

Voici donc All That Counts, une Web app qui contient :

  • Des timers.
  • Des chronos.
  • Des compteurs de points.

C’est du HTML5 + Javascript, il n’y a pas de backend. Est inclus un mode offline, donc il suffit de visiter l’app une fois pour pouvoir toujours l’utiliser sauf vidange du cache. Magie de Bootstrap, ça marche sur mobile, tablette et destop. Comprendre : design responsive générique.

Sur les navs supportant l’élément audio, ça gong à la fin des timers.

La page “Count Down” est pratique pour faire sa gym, cuire ses œufs et organiser une partie avec des tours limités dans le temps pour 3 joueurs ou plus. Les widgets incluent le temps de dépassement si on a besoin de mettre des pénalités aux escargots et aux stallers.

La partie “Chrono”, c’est gadget pour moi, mais ça pourra peut être servir à certains. J’y ai collé la possibilité de cumuler des temps de tours, pour les sportifs. Je ne sais pas si c’est utile, les coureurs me diront. A la limite pour les concours d’apnée…

L’onglet “Counter” c’est pour compter les points quand on a pas de papier ou de jeton, c’est du dépannage. Ça peut servir aussi pour les gens dont le métier implique de compter des têtes de pipe comme les videurs, les hôtesses de l’air, etc.

Ce qu’on utilise le plus est le mode “Versus”, qui sert aux duels. C’est une sorte de pendule d’échecs, avec plusieurs comportements réglables. Par défaut, le passage d’un décompte à l’autre se fait automatiquement et manuellement, chaque changement de joueur impliquant une remise à zéro.

Comme il n’y a pas de backend, vous pouvez avoir le code source en faisant Ctrl + S, donc je vais pas vraiment me faire chier à le mettre sur github. Déjà, j’ai du codé en pur JS. Moi. Heureusement qu’il y a AngularJS, sinon je changeais de métier. Le déploiement, pareil, c’est Ctrl + C, Ctrl + V, donc je vais pas écrire de doc. L’utilisation, bon, c’est des clics sur des boutons labellisés…

Le code source est dispo sur github.

La seule astuce, c’est qu’en mode versus, CTRL permet de faire un switch, et SPACE permet de faire pause et resume. Je pense que vous vous en sortirez. C’est un compteur, pas une navette spatiale.

Si l’envie incroyable de rajouter des features vous prend, mettez un comment, et je me bougerais le cul pour githuber tout ça.

]]>
http://sametmax.com/not-invented-here/feed/ 19
Utiliser AngularJs avec le langage de template de Django 9 http://sametmax.com/utiliser-angularjs-avec-le-langage-de-template-de-django/ http://sametmax.com/utiliser-angularjs-avec-le-langage-de-template-de-django/#comments Tue, 06 Nov 2012 14:29:57 +0000 http://sametmax.com/?p=2879 {{}}, exactement comme le langage de template Django. Cela amène à un conflit.]]> AngularJS est vraiment une bibliothèque javascript prometteuse. Je suis en train de jouer avec, avec l’espoir qu’un jour elle remplace complètement jQuery + Backbone.js dans tous mes projets.

Une chose qui peut être ennuyeuse cependant, c’est que par défaut Angular évalue les expressions qui sont marquées par {{}}, exactement comme le langage de template Django. Cela amène à un conflit.

Heureusement on peut changer ces marqueurs à l’initialisation:

var m = angular.module('monApp', []); 
m.config(function($interpolateProvider) { 
    $interpolateProvider.startSymbol('[['); 
    $interpolateProvider.endSymbol(']]'); 
});

Ici je choisis [[]].

]]>
http://sametmax.com/utiliser-angularjs-avec-le-langage-de-template-de-django/feed/ 9