I have joined Anti-IF Campaign

Connaissez-vous le nouveau framework javascript made in Google ?

C'est AngularJs : http://angularjs.org/.
Encore un me direz-vous ?
Eh bien oui mais je pense vraiment qu'il vaut le détour.

Sous licence MIT, AngularJs permet de mettre en place un comportement mvc coté client.
En deux coups de cuillère à pot, vous pouvez mettre en place un binding dynamique bi-directionnel entre la vue et le modèle de sorte que toute modification sur l'un soit visible sur l'autre. Jqxb propose le même genre de fonctionnalité mais AngularJs est plus simple à mettre en œuvre et semble plus robuste.
AngularJs offre aussi en 76ko (minifié) tout ce qu'il faut pour communiquer en ajax avec le serveur.
Je ne détaillerai pas plus toutes ses possibilités mais vous propose de le mettre en pratique.

L'objectif de ce tutorial est de réaliser un gestionnaire de liste de course.



1ere étape : afficher la liste des articles avec leur désignation, quantité et prix.
On commence par créer une page html se basant sur le bootstrap twitter.
On ajoute ensuite les ressources liées à AngularJs et on déclare la page comme étant une application gérée par AngularJs (ng-app dans le tag html).

<!doctype html>
<html lang="fr" ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Liste de course</title>
<link href="bootstrap.min.css" rel="stylesheet" />
<link href="bootstrap-responsive.min.css" rel="stylesheet" />
<script src="angular-1.0.0.js"></script>
</head>
<body>

     <div class="navbar">
       <div class="navbar-inner">
         <div class="container">
           <a class="brand" href="#">Liste de course</a>
         </div>
       </div>
     </div>

  <div class="container">
   ...
  </div>

</body>
</html>

On passe en suite à la partie JavaScript ou on va créer notre contrôleur qui, pour simplifier notre tutorial, inclura directement les données. Pour se faire, on définit une fonction avec un argument en entrée ($scope). On utilise cet argument qui nous est passé par le framework pour y ajouter une propriété (articles) qui contiendra nos données au format Json. Voici le résultat :

function malistedecourseCtrl($scope) {

  // Modele ................................................
  $scope.articles = [ {
   designation : 'beurre',
   quantite : 0,
   prix : 2.5
  }, {
   designation : 'lait',
   quantite : 0,
   prix : 0.23
  } ];

}

C'est tout pour la partie Javascript, simple non ?
On n'a plus qu'à boucler sur les données disponibles en jouant sur le code html :

<div ng-controller="malistedecourseCtrl" class="container">
  <table class="table">
   <thead>
    <tr>
     <th>Designation</th>
     <th>Quantite</th>
     <th>Prix</th>
    </tr>
   </thead>
   <tbody>
    <tr ng-repeat="article in articles">
     <td><input type="text" ng-model="article.designation" size="30" /></td>
     <td><input type="text" ng-model="article.quantite" size="3" /></td>
     <td><input type="text" ng-model="article.prix" size="4" /></td>
    </tr>
   </tbody>
  </table>
</div>

Les éléments importants du code ci-dessus sont :
- La référence à notre contrôleur au niveau de la div (ng-controller="malistedecourseCtrl")
- La déclaration de la boucle pour chaque élément de la liste (ng-repeat="article in articles"), notez le pseudo-langage utilisé pour définir la liste et l'élément courant
- La mise en place du binding automatique en faisant référence à une clé du modèle (ng-model="article.prix") sur l'élément courant
Le code est vraiment clair et les attributs utilisées sont explicites.

2eme étape : pouvoir ajouter un article
Corsons un peu notre liste de course en permettant d'ajouter un article à la liste.
Pour cela on va d'abord ajouter le formulaire suivant dans la div conteneur de la page html.

<form ng-submit="ajouterArticle()">
  <table class="table">
   <tbody>
    <tr>
     <td><input type="text" ng-model="designation" size="30" placeholder="nouvel article" /></td>
     <td><input type="text" ng-model="quantite" size="3" placeholder="quantite" /></td>
     <td><input type="text" ng-model="prix" size="4" placeholder="prix" /></td>
     <td><input class="btn-primary" type="submit" value="Ajouter" /></td>
    </tr>
   </tbody>
  </table>
</form>

Ce qu'il faut noter sur ce code :
- La déclaration de la méthode qui sera appelée lors de la soumission du formulaire (ng-submit="ajouterArticle()")
- Le rattachement de chaque champ à une donnée du modèle (ng-model="…")

Pour finir le travail, nous devons maintenant créer la fonction JavaScript « ajouterArticle ».

$scope.ajouterArticle = function() {
  $scope.articles.push({
   designation : $scope.designation,
   quantite : $scope.quantite,
   prix : $scope.prix
  });
  $scope.designation = '';
  $scope.quantite = '';
  $scope.prix = '';
};

Cette fonction est ajoutée en tant que propriété du $scope et permet, toujours en s'appuyant sur ce même $scope, de récupérer les données du formulaire, les ajouter directement dans la liste json des articles et enfin réinitialiser les champs de ce formulaire.
Une fois en place vous pouvez observer que l'ajout d'un nouvel article dans le modèle provoque dynamiquement la mise à jour de la vue.
Magique non ?

3eme étape : pouvoir supprimer un article
Si on peut ajouter un article, on doit aussi pouvoir en supprimer un …
Pour cela on commence par rajouter un bouton sur chaque article de notre liste.
Ce bouton sera relier à une fonction javascript permettant de réaliser la suppression (ng-click="supprimer(article)") :

<tr ng-repeat="article in articles">
  <td><input type="text" ng-model="article.designation" size="30" /></td>
  <td><input type="text" ng-model="article.quantite" size="3" /></td>
  <td><input type="text" ng-model="article.prix" size="4" /></td>
  <td><input class="btn-primary" type="button" ng-click="supprimer(article)" value="Supprimer" /></td>
</tr>

Définissons maintenant cette fonction dans la partie JavaScript.

$scope.supprimer = function(articlecurrent) {
  var cpt = 0;
  angular.forEach($scope.articles, function(article) {
   if (articlecurrent.designation === article.designation) {
    $scope.articles.splice(cpt, 1);
   }
   cpt = cpt + 1;
  });
};

La démarche est simple : on parcourt la liste et si la désignation correspond on supprime l'article correspondant dans la liste json.
Suite à cette suppression dans le modèle, la vue se met alors automatiquement à jour.
Cette fonction n'est surement pas très optimisée mais elle marche. Si vous avez une méthode plus efficace, n'hésitez pas à m'envoyer un commentaire.

4eme étape : afficher le nombre d'article et le prix total.
Dernière étape, on va présenter le nombre d'articles et le prix total de la liste.
Commençons par développer une fonction JavaScript permettant de calculer le prix total.

$scope.total = function() {
  var somme = 0;
  angular.forEach($scope.articles, function(article) {
   somme += article.prix * article.quantite;
  });
  return somme;
};

Cette fonction utilise les api du framework Angularjs (angular.forEach). Elle parcourt toute la liste, multiplie la quantité par le prix unitaire.de chaque élément et somme tous les résultats.
La fonction peut alors être utilisée directement dans le code html en suivant une syntaxe particulière :

<span><b>Liste en cours : {{articles.length}} articles pour un prix total de : {{total()}} </b></span>

Dans ce code on affiche le nombre d'articles dans la liste ({{articles.length}}) et le coût total ({{total()}}).
Si vous ajoutez, supprimez ou modifiez un article, vous verrez automatiquement ces informations mises à jour.

Voici le résultat final :

<!doctype html>
<html lang="fr" ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Liste de course</title>
<link href="bootstrap.min.css" rel="stylesheet" />
<link href="bootstrap-responsive.min.css" rel="stylesheet" />
<script src="angular-1.0.0.js"></script>
<script src="malistedecourse.js"></script>
</head>
<body>

     <div class="navbar">
       <div class="navbar-inner">
         <div class="container">
           <a class="brand" href="#">Liste de course</a>
         </div>
       </div>
     </div>

  <div ng-controller="malistedecourseCtrl" class="container">
   <table class="table">
    <thead>
     <tr>
      <th>Designation</th>
      <th>Quantite</th>
      <th>Prix</th>
     </tr>
    </thead>
    <tbody>
     <tr ng-repeat="article in articles">
      <td><input type="text" ng-model="article.designation" size="30" /></td>
      <td><input type="text" ng-model="article.quantite" size="3" /></td>
      <td><input type="text" ng-model="article.prix" size="4" /></td>
      <td><input class="btn-primary" type="button" ng-click="supprimer(article)" value="Supprimer" /></td>
     </tr>
    </tbody>
   </table>

   <span><b>Liste en cours : {{articles.length}} articles pour un prix total de : {{total()}} </b></span>
  
   <hr/>
  
   <form ng-submit="ajouterArticle()">
    <table class="table">
     <tbody>
      <tr>
       <td><input type="text" ng-model="designation" size="30" placeholder="nouvel article" /></td>
       <td><input type="text" ng-model="quantite" size="3" placeholder="quantite" /></td>
       <td><input type="text" ng-model="prix" size="4" placeholder="prix" /></td>
       <td><input class="btn-primary" type="submit" value="Ajouter" /></td>
      </tr>
     </tbody>
    </table>
   
   </form>
  </div>

</body>
</html>

function malistedecourseCtrl($scope) {

  // Modele ................................................
  $scope.articles = [ {
   designation : 'beurre',
   quantite : 0,
   prix : 2.5
  }, {
   designation : 'lait',
   quantite : 0,
   prix : 0.23
  } ];

  // Fonctions ................................................
  $scope.ajouterArticle = function() {
   $scope.articles.push({
    designation : $scope.designation,
    quantite : $scope.quantite,
    prix : $scope.prix
   });
   $scope.designation = '';
   $scope.quantite = '';
   $scope.prix = '';
  };
 
  $scope.supprimer = function(articlecurrent) {
   var cpt = 0;
   angular.forEach($scope.articles, function(article) {
    if (articlecurrent.designation === article.designation) {
     $scope.articles.splice(cpt, 1);
    }
    cpt = cpt + 1;
   });
  };

  $scope.total = function() {
   var somme = 0;
   angular.forEach($scope.articles, function(article) {
    somme += article.prix * article.quantite;
   });
   return somme;
  };

}

En conclusion :
C'est un tres bon outil, je regrette juste que les attributs ne respectent pas les data-* html5.

A+

Références :
angularjs
twitter bootstrap
jqxb

Vous pouvez trouver le code complet en suivant l’url : code
Vous pouvez trouver la démo en suivant l’url : demo

Aucun commentaire:

Enregistrer un commentaire