Chargement...

Angular JS le framework Javascript Documentation


angularjs

Angular JS c'est quoi?

AngularJS est un framework écrit en javascript par Google libre et open-source qui permet d'améliorer, au même titre que JQUERY, la syntaxe de javascript ainsi que la productivité du développeur. Angular en basé sur la logique MVC. (modèle vue controller). Crée en 2009 par Google ce framewok est devenu incontournable pour les applications web.

AngularJS utilise jQuery mais vous n'êtes pas obligé de comprendre jquery pour utiliser AngluarJS.

Pourquoi utiliser AngularJS

AngularJS n'est pas juste une library qui propose de nouvelles fonctionnalités mais est un framework puissant et facile à prendre en main qui encadre le développeur tout en proposant une manière propre de programmer avec une logique MVC.

L'une des principales raisons d'utiliser AngularJS c'est de pouvoir construire un projet en gardant à l'esprit les tests. L'approche test est l'une des notions les plus importantes puisqu'il permet de conserver un code maintenable, compréhensible et sans bug. Une bonne suite de tests peut aider à trouver les problèmes avant que votre projet ne soit en production. Cela rendra votre logiciel plus fiable, peut être plus amusant et cela vous aidera à dormir sur vos deux oreilles la nuit. AngularJS s'annonce indispensable pour les projets ambitieux puisqu'il vous propose une organisation très bien conçu de base. Alors bien évidemment on parle de projet un peu plus complexe qu'un simple site web, comme celui-ci par exemple qui demande que très peu de connaissance en javascript/jquery. On utilise AngularJS plus des web applications bien entendu.

Vous avez peut être déjà travaillé (ou essayé) de travailler sur un projet / application web qui comporte une seule page et ayant comme ambition de charger chaque module en ajax. Au passage en informatique il ya un nom pour tout (et surtout des acronymes), ce concept de web application à une seule page se nomme SPA (pour single-page application). Rien de fou, l'idée est simple et plus ou moins facile. Avez-vous pensé à gérer l'historique de navigation pour revenir à la page précédente? Mais si a chaque projet vous devez recréer tout le mécanisme pourquoi réinventer la roue à chaque fois alors d'autres ont déjà mis les outils en place pour travailler de la manière la plus propre possible. Et pourquoi se priver d'un outil reconnu par la communauté des développeurs et surtout un outil qui facile l'intégration de nouveau programmeur dans votre team.

Installer AngularJS

Pour pouvoir utiliser angularjs, il vous suffit d'intégrer ce code entre les balises HEAD de votre page HTML:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>

Vue et template

La vue est une projection du modèle à travers un template HTML.

<!DOCTYPE html>
<html ng-app="facture">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
</head>
<body ng-controller="FactureController">
  <ul>
    <li ng-repeat="product in products">
      <span>{{product.name}}</span>
      <p>{{product.description}}</p>
    </li>
  </ul>
</body>
</html>

Cet exemple nous permet de décrire deux concepts AngularJS: les directives et les data binding.

+ Le premier concept visible sont les directives. Dans notre exemple "ng-app" (data-ng-app fonctionnerait aussi) ou "ng-controller" ou encore "ng-repeat" sont typiquement des directives que l'on utilisera très souvent. Alors c'est quoi une directive? Une directive est un marqueur sur un élément du DOM (comme un attribute, un nom d'élément, un commentaire ou une classe CSS) qui dit au compilateur HTML d'AngularJS ($compile) d'associer un comportement spécifique au DOM en question et à ses enfants. On peut dire qu'il s'agit d'un comportement associé à un trigger dans le DOM. La logique est l'inverse de celle de jQuery. Avec jQuery on affecte un comportement sur une sélection d'élement alors qu'avec AngularJS on définit d'abord les comportements puis on les associé au niveau du code HTML.

+ le deuxième concept à assimiler est le data-binding. C'est quoi le data binding? Le data-binding dans les applications AngularJS est une synchronisation automatique de données entre le modèle et les composants de la vue. La vue est une projection du modèles en temps réel. Quand le modèle change, la vue affiche immédiatement les changement et inversement. Dans notre exemple {{ product.name }} et {{ product.description }} sont des data binding.

Modèle et controlleur

Les données du modèle (une liste de produits) est maintenant instancié dans le controlleur FactureController. Le controlleur est une simple fonction constructeur qui prend le paramètre $scope

Exemple:


var product = angular.module('facture', []);
product.controller('FactureController', function ($scope) {
  $scope.products = [
    {'name': 'ipad 1',
     'description': 'Super ipad'},
    {'name': 'ipad 2',
     'description': 'Un ipad encore mieux'},
    {'name': 'ipad 3',
     'description': 'Alors celui la il est encore mieux.'}
  ];
});

Résultat:

Photofiltre

La directive ng-model

La directive ng-model permet d'associer un élément du DOM à un data binding:

<input ng-model="customer_name" type="text" placeholder="Nom du client">
<h1>Bonjour {{ customer_name }}</h1>

Résultat

Photofiltre

Route

Une des fonctionnalités les plus intéressante est le système de route que propose le framework AngularJS.

Créons une page HTML qui service de base dans notre projet avec une architecture SPA:

<!DOCTYPE html>
<html lang="fr">
  <head>
    <title>Mon projet SPA</title>
  </head>
  <body ng-app="mon_app">
    <div ng-view></div>
    <script src="angular.min.js"></script>
  </body>
</html>

Nous avons donc définit l'application au niveau de la balise body (qu'importe l'endroit en fait) et nous avons crée une balise div qui recevera les données HTML en ajax, pour cela nous lui avons attribué la directive ng-view. C'est tout ce qu'il y a faire du côté de la vue.

Il existe pour info d'autres syntaxes pour la directive ng-view:

<div ng-view></div>
<div data-ng-view></div>
<ng-view></ng-view>
<div class="ng-view"></div>

Pour la partie javascript, vous pouvez intégrer le code suivant:

var mon_app = angular.module('mon_app', []);
 
mon_app.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/page1', {
        templateUrl: 'templates/page1.html',
        controller: 'PageController'
    }).
      when('/page2', {
        templateUrl: 'templates/page2.html',
        controller: 'Page2Controller'
      }).
      otherwise({
        redirectTo: '/page1'
      });
}]);

Ce script associe une page à un template et un controlleur. Cela permet par exemple de garder un même controlleur pour un template différent, on peut penser par exemple au cas du template par type de support (iphone, ipad, bureau) mais surtout de garder à l'esprit un mode de développement type DRY (Don't repeat yourself) qui permet d'éviter la redondance de données. De manière générale vous trouverez beaucoup de similitudes dans la logique de programmation avec le framework Django de python.

Attention si vous utilisez d'autres config, pensez à les combiner ensemble:

mon_app.config(function($routeProvider, $interpolateProvider) {
      
  $interpolateProvider.startSymbol('~{');
  $interpolateProvider.endSymbol('}~');

  $routeProvider.
  when('/dashboard', {
    templateUrl: 'page/dashboard',
    controller: 'PageController'
  }).
  when('/caisse/:id', {
    templateUrl: 'page2/caisse',
    controller: 'Page2Controller'
  }).
  otherwise({
    redirectTo: 'page1'
  });
  
});

Alors comme vous pouvez le remarquer, en quelques ligne de code vous êtes prêt à créer votre application SPA qui comprend en compte tous les besoins utilisateurs comme l'historique, etc.

Route et paramètres

mon_app.config(function($routeProvider, $interpolateProvider) {
   
  $routeProvider.
  when('/page1/:id', {
    templateUrl: 'page/page1',
    controller: 'PageController'
  }).
  when('/page2/:id', {
    templateUrl : function(params){ return '/page/page2/' + params.id; }, 
    controller: 'TicketController'
  }).
  otherwise({
    redirectTo: '/dashboard'
  });
});

Vous pouvez ensuite récupérer le paramètre comme ceci:

mon_app.controller('MonController', function ($scope, $routeParams) 
{
  var id = $routeParams.id;
}

Ecouter une variable

Une fonctionnalité vraiment utile est l'utilisation de $watch qui permet d'écouter une variable. Si elle venait à être modifiée, une peut par exemple exécuter une fonction

  $scope.$watch("ma_variable", function(){
      $scope.une_action();
  }, true);

On utilisera la valeur true comme deuxieme paramètre pour indique qu'on écoute la variable de manière récursive (dans le cas des tableau est des objects)

Ecouter un évènement

Vous pouvez intervenir lors de l'exécution d'un évènement.

J'ai été confronté à un bug lors de l'utilisation du plugin jquery select2 que je voulais faire cohabiter avec angularjs. Un conflit est vite apparu, il a donc fallu trouver un moyen d'exécuter la fonction select2 apres le chargement du controller angularjs. Pour cela j'ai écouté l'évènement $viewContentLoaded

  $scope.$on('$viewContentLoaded', function(){
    $('select').select2();
    alert('Executée au chargement de la page');
  });

Récupérer le keyCode (action d'une touche clavier)

Vous serez à un moment ou un autre amener à détecter si l'utilisateur a appuyé sur une certaine touche pour faire une certaine action.


<input ng-keyup="search($event)" />

$scope.search = function($event)
{
  if( $event.keyCode == 13 )
      alert( "Touche entrée!" );
}

Select / options

Si le cas du binding est évidement pour les inputs en général pour le cas du select il l'est beaucoup moins:

<select  
ng-init="users =[{username: 'olivier', id: 1 }, {username: 'engel', id : 2}]" 
ng-model="id_user" 
ng-options="user.id as user.username for user in users">
</select>

Alors dans ce cas nous avons opté de charger le select avec la directive ng-init. Il est tout a fait possible d'indiquer au select les valeurs à rentrer depuis le controller:

$scope.users = [{username: 'olivier', id: 1 }, {username: 'engel', id : 2}];

Cette seconde utilisation est pour des données statiques.

Ajax

Voici une méthode pour appeller une ressource en ajax:

$http.get("/data.json").success(function (data, status, headers, config) {

    // On peut utiliser $scope
    $scope.nbr = data.nbr;

}).error(function (data, status, headers, config) {
    console.log(status);  
}); 

Ajax et Symfony

Pour envoyer un formulaire au framework Symfony on utilisera la méthode suivante:

var data = {};
data.name = "ENGEL";

$http({
    method: "POST",
    url: url,
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    data: $.param(data)
});

ngResource / API REST

Si votre application est API REST vous pouvez utiliser le module resource

Ajoutez le module dans votre application

app = angular.module('awesome', ['ngResource']);

Et dans votre controleur:

Seller = $resource('/api/sellers/:sellerId', {sellerId:'@id'});
$scope.seller = Seller.get( {sellerId : 5} );

N'oubliez pas d'injecter $resource

Usage de $resource:
$resource(url, [paramDefaults], [actions], options);

Vous pouvez également utiliser les services AngularJS:

mg.factory 'Seller', ($resource) ->
  return $resource '/api/sellers/:id' , {id: '@id'}, 
  {
    query: { method: 'GET', isArray: true },
    save: { method: 'POST', headers: { 'Content-Type': 'application/json' }, },
    show: { method: 'GET' },
    update: { method: 'PUT', params: {id: '@id'} },
    delete: { method: 'DELETE', params: {id: '@id'} }
  }

mg.service 'service_seller', ($resource, Seller) ->

  @get = (id) ->
    return Seller.get( {id : id} );

  @create = ->
    seller = new Seller()
    seller.username = 'Olivier ENGEL'
    seller.$save().then(() -> 
      console.log seller.id
    );

  @update = (id) ->
    seller = Seller.get({id: id}, () -> 
      seller.username = "Zorro"
      seller.$update()
    )

  @$delete = (id) ->
    Seller.delete({id: id})

  return

Les tests unitaires

Ce qui est le plus appréciable avec angularJS, ce sont certainement les tests unitaires qui sont très bien pensés.

Installons Karma pour effectuer nos tests unitaires

npm install karma --save-dev

Puis les plugins nécessaires à son utilisation

npm install karma-jasmine --save-dev
npm install karma-phantomjs-launcher --save-dev

Créons notre fichier de configuration karma que nous nommerons karma.conj.js (peu importe son emplacement)

// Karma configuration
module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '..',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [

     
      'public/bower_components/angular/angular.js',
      'public/bower_components/angular-route/angular-route.js',
      'public/bower_components/angular-resource/angular-resource.js',
      'public/bower_components/angular-mocks/angular-mocks.js',
      'public/js/global.js',
      'tests/front.js'
    ],

    plugins : ['karma-jasmine', 'karma-phantomjs-launcher'],


    // list of files to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9999,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};

Exemple de code app angularjs

var mon_module = angular.module('mon_module', []);

mon_module.controller('FrontController', function ($scope, $http) {

  $scope.nbr = 0;

  $scope.save_me_barry = function() 
  {
    $http.post('/test').success(function(data) {
      $scope.nbr = data.nbr;
    });
  }
});

Exemple de test unitaire

'use strict';
describe('Front Test', function() {

  beforeEach(module('mon_module'));

  var controller, $scope, http, httpBackend;

  describe('with httpBackend', function() {

    beforeEach(inject(function($controller, $rootScope, $httpBackend, $http, $location) {

      $scope = $rootScope.$new();
      
      controller = $controller('FrontController', { $scope: $scope });
      httpBackend = $httpBackend;
      http = $http;

      $scope.save_me_barry();
      $httpBackend.expectPOST('/test').respond({"nbr":123});
      $httpBackend.flush();

    }));
    
    it('Test du retour', function() {

      expect($scope.save_value).toEqual(123);

    });
  });
});

Et enfin exécutons nos tests unitaires:

./node_modules/karma/bin/karma start

Liens utiles

www.ng-newsletter.com
docs.angularjs.org




UNE QUESTION SUR L'ARTICLE?


CSSW Apprendre à créer son site web CSSW