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 :
- Vous déclarez un contrôleur et vous le liez à du HTML via
ng-controller
. - Dans ce contrôleur vous dites “j’ai besoin du service Machin”
- Angular cherche s’il connaît Machin. Si non, il plante.
- 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.
Article intéressant, j’ai du employer angular au sein d’un stage, c’est vrai que c’est déroutant quand le seul framework que l’on a connu est Jquery. Rapidement on se rend compte de l’avantage qu’il offre pour la logique de présentation de l’app, notamment au travers de l’utilisation de ng-repeat et etc… qui torche pas mal de taf.
Il n’empêche que je suis loin encore de tout comprendre, entre les directives, les factories et etc… bref, encore un tas de choses à maitriser.
cool pour l’article :) merci
Merci pour cette deuxième partie, c’est très clair et instructif.
Jolie contrepétrie dans la phrase d’intro, alors c’est fini le tennis en pension?
Très bon article, en revanche c’est ng-controller et pas ng-controlleur ^^
Ce ne sont pas les droids que vous cherchez.
Excellent article. Vraiment. J’ai (enfin!) compris l’utilité d’Angular un grand merci!
(et vivement la suite !)