Débuter avec AngularJS – Comprendre les bases #1

AngularJS est un framework Javascript puissant qui nous permet de faciliter l’organisation, le maintient et d’enrichir non seulement notre code JS mais également notre code HTML.

Nous allons aborder les notions principales d’AngularJS, de la façon la plus simple possible car le but de ce chapitre est de comprendre le fonctionnement global. Pour cela, le fil rouge sera le développement d’une boutique en ligne.

Dans ce chapitre, nous aurons simplement 2 fichiers, index.html pour le rendu visuel, et app.js qui contiendra la logique de notre petite application.

Les modules

Une application angular se compose de module, un conteneur commun à toutes les parties de l’application (services, contrôleurs, directives etc…) qui permet de configurer l’injecteur (AngularJS encourage le faible couplage, nous verrons l’injection dans un chapitre ultérieur).

Créons donc un fichier nommé app.js qui sera le point d’entrée de l’application et déclarera notre premier module qui portera le nom de myStore.

//app.js
var app = angular.module("myStore", []);

 Et couplons-le au fichier d’entrée HTML de notre single page application grâce à la directive ng-app.

//index.html
<!DOCTYPE html>
<html ng-app="myStore">
</html>

Les contrôleurs

La logique métier derrière les vues sont organisées par contrôleurs, pour cela nous allons créer un premier contrôleur qui sera le principal de l’application.

//app.js
app.controller("storeController", function(){
    this.product = { 
                       id : 001, 
                       label : "my 1st product", 
                       description : "description of my product", 
                       price : 2.50, 
                       available : true};
});

Et le déclarer avec sa porté dans notre fichier HTML via la directive ng-controller.

//index.html
<body>
    <div class="container" ng-controller="storeController as storeCtrl">
         <div class="product">
             <span>{{ storeCtrl.product.label }}</span>
         </div>
    </div>
</body>

Note: En déclarant le controller « storeController », je lui ai donné un alias pour simplifier son appel par la suite.
Du coup l’appel storeController.product devient storeCtrl.product

Note 2 : Si je tente d’appeler storeCtrl.product en dehors de sa porté, le div où il est déclaré, cela provoquera une erreur.

Les directives

Les directives étendent HTML grâce à des attributs et des éléments personnalisés. Dans notre cas, nous voulons afficher le bouton « Add to Cart » uniquement si le produit est disponible.
Pour cela, nous allons ajouter une condition (expression) dans la directive ng-show, et ce, directement dans le code HTML !

//index.html
<div class="container" ng-controller="storeController as storeCtrl">
    <div class="product">
        <span>{{ storeCtrl.product.label }}</span>
        <button ng-show="storeCtrl.product.available == true">Add to cart</button>
    </div>
</div>

Note : Si la propriété « available » du produit est indéfinie, une erreur ne sera pas remonté mais tout simplement la valeur sera différente de « true » donc le bouton ne sera pas affiché.

Note 2 : ng-hide, ng-repeat, ng-class et bien d’autres sont également des directives qui nous facilitent la vie !

Les expressions

Maintenant que nous avons vu le concept de directive, nous allons pouvoir gérer facilement plusieurs produits ! Nous avons utilisé des conditions dans nos directives, ces expressions angular permettent de définir comment les valeurs seront affichées dans la page en accédant aux variables et fonctions du scope (la notion de scope étant un peu complexe, je lui réserve un chapitre dédié ^^).

Changeons un peu notre code pour avoir un tableau d’objets produit…

//app.js
app.controller("storeController", function(){
    this.products = [
                        { id : 001, 
                          label : "my 1st product", 
                          description : "description of my product", 
                          price : 2.50, 
                          available : true},
                        { id : 002, 
                          label : "my 2nd product", 
                          description : "description of my product", 
                          price : 10.50, 
                          available : false},
                        { id : 003, 
                          label : "my 3td product", 
                          description : "description of my product", 
                          price : 7.00, 
                          available : true}
                   ];
});

Coté HTML, nous allons utiliser une directive, ng-repeat, pour lister tous les produits.

//index.html
<div class="product" ng-repeat="product in storeCtrl.products">
     <span>{{product.label }}</span>
     <button ng-show="product.available == true">Add to cart</button>
</div>

Par la directive ng-repeat, nous demandons “pour chaque “product” dans la collection “storeCtrl.products” de répéter cette section de code.

Les filtres

Les filtres angular permettent de formater une valeur d’une expression pour l’affichage.

Nous allons donc ordonner nos produits par prix, puis afficher et formater correctement nos prix selon la devise.

//index.html
<div class="product" ng-repeat="product in storeCtrl.products | orderBy:-price">
    <span>{{product.label }}</span>
    <i>{{product.price | currency}}</i>
    <button ng-show="product.available == true">Add to cart</button>
</div>

Il existe bien entendu beaucoup de filtres et nombreux prennent comme orderBy des paramètres supplémentaires, comme ici par prix (price) décroissant (-).

Le data-binding

AngularJS nous permet facilement de faire du data-binding dans les 2 sens, cela signifie que l’expression est ré-évaluée quand une propriété change aussi bien côté vue que contrôleur.
Pour illustrer ce concept, nous allons créer les classiques onglets « caractéristiques », « téléchargements et support » et « commentaires » par produit.

//index.html
<div class="product" ng-repeat="product in storeCtrl.products | orderBy:-price">
    <span>{{product.label }}</span>
    <i>{{product.price | currency}}</i>
    <button ng-show="product.available == true">Add to cart</button>
    <ul ng-init="tab = 1">
        <li ng-click="tab = 1">Caracteristics</li>
        <li ng-click="tab = 2">Downloads & Support</li>
        <li ng-click="tab = 3">Comments</li>
    </ul>
    <div class="panel" ng-show="tab === 1">
        {{tab}}
    </div>
    <div class="panel" ng-show="tab === 2">
        {{tab}}
    </div>
    <div class="panel" ng-show="tab === 3">
        {{tab}}
    </div>
</div>

Quelques explications… nous initialisons la valeur de « tab » grâce à la directive ng-init afin d’avoir une valeur par défaut. Via la directive ng-click, nous indiquons que l’évènement « click » sur un item de la liste met à jour la valeur de « tab », enfin pour finir, nous indiquons avec la directive ng-show quel panel afficher selon la valeur de « tab ».

Pour aller un peu plus loin dans l’utilisation des directives, nous aimerions aussi savoir quel onglet est actif afin de lui associer une classe css. Vous l’aurez deviné, nous allons utiliser une directive … ng-class !

//index.html
<div ng-repeat="product in storeCtrl.products | orderBy:-price">
    <span>{{product.label }}</span>
    <i>{{product.price | currency}}</i>
    <button ng-show="product.available == true">Add to cart</button>
    <ul ng-init="tab = 1">
        <li ng-click="tab = 1">Caracteristics</li>
        <li ng-click="tab = 2">Downloads & Support</li>
        <li ng-click="tab = 3">Comments</li>
    </ul>
    <div ng-show="tab === 1" ng-class="{active: tab === 1}">
        {{tab}}
    </div>
    <div ng-show="tab === 2" ng-class="{active: tab === 2}">
        {{tab}}
    </div>
    <div ng-show="tab === 3" ng-class="{active: tab === 3}">
        {{tab}}
    </div>
</div>

Note : Une autre façon d’écrire l’expression de la directive ng-class aurait été par exemple :
ng-class= »{true:’active’, false : ‘normal’}[tab === 1]”

Nous avons fait le tour du chapitre, sans aller trop loin mais en prenant le temps de voir un peu comment utiliser les concepts de base 🙂

Notre application a donc un module en entrée nommé « myStore » déclaré dans notre code HTML via la directive ng-app, tout comme notre contrôleur « storeController » alias « storeCtrl » avec ng-controller. Nous avons ajouté de la logique d’affichage dans notre code HTML via les directives ng-show, ng-hide, ng-repeat et ng-class, avec les expressions et les filtres ainsi qu’abordé le système de binding à double sens d’angular avec la notamment ng-init et ng-click qui modifient « tab » directement depuis le code HTML.

Ce code fonctionne correctement mais commence doucement à devenir difficilement lisible, nous allons voir comment le garder propre, lisible et facile a maintenir dans le prochain article.