AngularJS

Transcendez vos développements Web avec AngularJS

- Hands On -

Thierry Lau - @laut3rry

SFEIR

front-end developer

Anime la page HTML5 * CSS3 - ʕ๏๏ʔ

et la communauté HTML5 * CSS3 [FR]

Pré-requis

  • un client git pour récupérer le code source
  • node.js pour lancer l'application
  • un navigateur Chrome, Firefox ou IE9+
  • votre éditeur de code favori

Récupérer les slides

http://lauterry.github.com/slides-handson-angular

Récupérer le code

git clone https://github.com/lauterry/angularmovie.git

ou

Récupérer à partir de Google Drive

Déroulement

Présentation des concepts clé d'AngularJS ...

... et 14 exercices pour les mettre en pratique

Simples pages HTML statiques

Simples pages HTML statiques

Application Web Moderne

Application Web Moderne

    Pour chaque concept :

  1. Présentation
  2. Enoncé de l'exercice
  3. A vous de jouer !
  4. Live coding de la réponse + explications

A la traine ?

14 exos = 14 branches git

git checkout -f step-#

C'est parti !

git checkout -f step-0

Initialisation et Double Data Binding

Charger angularjs dans votre page ...


<html>
    <head>
        <script src="path/to/angular.js"></script>
    </head>
    <body>

    </body>
</html>
                        

... le rendre actif sur celle-ci ...


<html ng-app>
    <head>
        <script src="path/to/angular.js"></script>
    </head>
    <body>

    </body>
</html>
                        

... et vérifier que ca marche


<html ng-app>
    <head>
        <script src="path/to/angular.js"></script>
    </head>
    <body>
    {{ 1 + 1 }}
    </body>
</html>
                        

Bonjour {{greeting}}

JavaScript


<html>
    Bonjour <span id="name"></span>
    <input type="text"/>
</html>
                            

window.onload = function(){

    var span = document.getElementById('name');
    var input = document.getElementsByTagName('input')[0];

    input.onkeyup = function(){
        if (span.textContent || span.textContent === "") {
            span.textContent = input.value;
        } else if(span.innerText || span.innerText === "") { // IE
            span.innerText = input.value;
        }
    };
};
                            

jQuery


<html>
    Bonjour <span id="name"></span>
    <input type="text"/>
</html>
                        

$(document).ready(function() {
    var $input = $('input');
    var $span = $('#name');

    $input.keyup(function (event) {
        $span.text(event.target.value);
    });
});
                        

AngularJS


<html ng-app>
    <div>
        <input type="text" ng-model="name">
        <p>Bonjour {{name}}</p>
    </div>
</html>
                        

Exercice 1 : Saluez qui vous voulez !

Changer dynamiquement le nom par le contenu d'un champ de saisie

  • Pensez à initialiser AngularJs dans la page index.html
  • La librairie angularjs est disponible dans le dossier js/lib
  • Rajouter un champ de saisie
  • Nommons par exemple la variable {{name}}
  • Remplacer le titre AngularMovie par {{name}}

Controller et Scope


<html ng-app>
    <div ng-controller="myController">
        <input type="text" ng-model="name">
        <p>Hello {{name}}</p>
    </div>
</html>
                        

function myController($scope){
    $scope.name = 'World';
}
                        

Exercice 2 : Importance du Scope

Gardez les mêmes variables {{name}} mais leur affecter des valeurs différentes

  • Créez un fichier app/js/app.js et y écrire vos 2 controllers
  • Attention à l'endroit où vous déclarez vos controllers dans la page HTML

Module & Namespacing


<html ng-app="myApp">
    <div ng-controller="myController">
        <input type="text" ng-model="greeting">
        <p>{{greeting}}</p>
    </div>
</html>
                        

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

myApp.controller('myController', function($scope){
    $scope.greeting = 'Hello World';
});
                        

Solution

git checkout -f step-1

Templating avancée

ng-repeat

Itère dans une collection et génére un template par élément


<ul>
    <li ng-repeat="fruit in fruits">
        /* répétition du template li par élément fruit */
        {{ fruit.name }}
    </li>
</ul>
                        

Exercice 3 : ng-répéter les films

Afficher la liste des films en utilisant la directive ng-repeat

  • AngularJs est déjà actif dans la page movie-list.html
  • Créer un nouveau contrôleur nommé moviesController pour la liste des films
  • Créer une collection de films dans le contrôleur. Aidez vous de la slide suivante !
  • Pensez à rendre dynamique le nombre de films affichés
  • JS : accéder aux propriétés d'un objet avec la notation pointée (ex : car.color)

[
    {
        "id":1,
        "title":"Avatar",
        "releaseYear":"2010",
        "poster":"img/avatar.jpg",
        "directors":"James Cameron",
        "actors":"Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang, Michelle Rodriguez",
        "synopsis":"Sur la lointaine planète de Pandora, Jake Sully, un héros malgré lui, se lance dans une quête de rédemption, de découverte, d'amour inattendu, dont l'issue sera un combat héroïque pour sauver toute une civilisation.",
        "rate":"3"
    },
    {
        "id":2,
        "title":"Seigneur des Anneaux : La Communauté de l'Anneau",
        "releaseYear":"2003",
        "poster":"img/seigneurdesanneaux1.jpg",
        "directors":"Peter Jackson",
        "actors":"Elijah Wood, Sean Astin, Ian McKellen, Sala Baker, Viggo Mortensen",
        "synopsis":"Frodon le Hobbit hérite de l'Anneau Unique, un instrument de pouvoir absoluqui permettrait à Sauron, le Seigneur des ténèbres, de régner sur la Terre du Milieu. Commence alors un vaste périple visant à la destruction de l'objet.",
        "rate":"5"
    },
    {
        "id":3,
        "title":"The Grudge",
        "releaseYear":"2004",
        "poster":"img/thegrudge.jpg",
        "directors":"Takashi Shimizu",
        "actors":"Sarah Michelle Gellar, Jason Behr, Clea DuVall, Kadee Strickland, Bill Pullman",
        "synopsis":"Dans ce qui paraît être une paisible maison de Tokyo se cache un épouvantable fléau. Quiconque franchit le seuil de la demeure est aussitôt frappé par une malédiction qui ne tardera pas à le tuer dans un sentiment d'indicible rage...",
        "rate":"4"
    },
    {
        "id":4,
        "title":"Yip Man 2",
        "releaseYear":"2010",
        "poster":"img/yipman.jpg",
        "directors":"Wilson Yip",
        "actors":"Donnie Yen, Sammo Hung Kam-Bo, Simon Yam, Lynn Hung, Xiaoming Huang",
        "synopsis":"Film biographique sur la vie de Ip Man, pionnier du Wing Chun et maitre de Bruce Lee.",
        "rate":"5"
    }
]
                        

Solution

git checkout -f step-2

Gestion des évènements

ng-click


<li ng-click="faireUnTruc(arg1, arg2, ...)"></li>
                    

function myController($scope){
    $scope.faireUnTruc = function(arg1, arg2, ...){
        /* faire un truc */
    };
}
                    

git checkout -f step-3

Ajout d'un modal Twitter Bootstrap

Exercice 4 : Gérer un click

Afficher une window.alert() avec quelques infos du films saisies dans le formulaire

  • Créer un nouveau controller nommé movieFormController pour gérer le formulaire
  • N'oubliez pas de binder les champs de saisies au $scope

Exercice 5 : Ajouter un film dans la liste

Ajouter un film dans la liste des films et l'afficher dans la page

  • Pensez à la hiérarchie des $scope
  • Surveillez la position de vos directives ng-controller
  • Pensez à réinitialiser le formulaire après chaque film rajouté
  • Utiliser la méthode array.push(object) pour ajouter le film dans le tableau $scope.movies

Solution

git checkout -f step-3-solution

Propagation des évènements $scope

Un évènement peut être "diffusé" aux $scope enfants ...


$scope.$broadcast('myEvent', param1, param2, ...);
                    

... ou "émis" aux $scope parents


$scope.$emit('anotherEvent', param1, param2, ...);
                    

L'écoute des évènements se fait comme suit :


$scope.$on('yetAnotherEvent', function(event, param1, param2, ...){
    console.log(param1);
});
                    

Exercice 6 : Ajouter un film dans la liste (autre solution)

Ajouter un film dans la liste des films et l'afficher dans la page en utilisant les évènements du scope

  • $emit ou $broadcast ?
  • Cette solution peut remplacer celle de l'exercice 5

Solution

git checkout -f step-3-scope-event

XHR & Injection de dépendances

Pour effectuer des appels XHR, Angular dispose du service $http ...

... injectable comme le $scope


function myController($scope, $http){

};
                        

Usages


$http.get(url, options)
                        

$http.post(url, data, options)
                        

$http.put(url, data, options)
                        

$http.delete(url, options)
                        

Gérer les retours


$http.get(url, options)
    .success(function(data, status, headers, config){
        /* do something */
    })
    .error(function(data, status, headers, config){
        /* handle error */
    });
                        

Exercice 6 : Fini les données en dur.

Récupérer les films à partir du serveur. Idem pour l'ajout de film

  • GET /server/api/movies
  • POST /server/api/movies
  • Attention au format du JSON reçu

Solution

git checkout -f step-4

Navigation côté client

http://localhost:3001/#/home

#/home

http://localhost:3001/#/movies

#/movies

http://localhost:3001/#/movies/edit/1

#/movies/edit/1

Service $routeProvider

Permet de configurer le template HTML à charger avec son contrôleur associé pour une URL donnée


$routeProvider
    .when('/route1', {
      templateUrl: 'path/to/view1.html',
        controller : 'view1Controller'
    })
    .when('/route2', {
        templateUrl: 'path/to/view2.html',
        controller : 'view2Controller'
    })
    .when('/route2/:id', {
        templateUrl: 'path/to/view3.html',
        controller : 'view3Controller'
    })
    .otherwise({
        redirectTo: '/route1'
    });
                    

Où l'injecter ?

Dans la factory config de votre module


/* Dans le fichier de définition de votre module app.js*/
var angularMovieApp = angular.module('angularMovieApp', []);

angularMovieApp.config(function($routeProvider) {
    /* routes configurations */
});
                    

A utiliser avec la directive ng-view


<body>
    <header>
        /* Common part for all pages.*/
    </header>
    <section ng-view>
        /* this is the dynamic part */
    </section>
    <footer>
        /* Common part for all pages.*/
    </footer>
</body>
                    

Attention : Vous ne pouvez utiliser qu'une seule directive ng-view

Exercice 6 : Une navigation côté client

La navigation d'une page à l'autre peut être améliorée. Utiliser le service $routeProvider et la directive ng-view pour ne garder qu'un fichier index.html

  • Identifier dans votre page index.html les portions qui sont communes à toutes les pages et la partie spécifique
  • Créer un répertoire app/partials où stocker les fragments de page spécifique
  • Utiliser soit l'attribut controller du service $routeProvider, soit la directive ng-controller
  • Penser à corriger les attributs href des liens de la barre de menu supérieure

Solution

git checkout -f step-5

Exercice 7 : Supprimer un film

git checkout -f step-5-delete

Un bouton de suppression vient d'être ajouté pour chaque film. Implémentez son action

  • API : DELETE /server/api/movies/:id
  • Astuce : Vous pouvez accéder au numéro d'index courant de la collection dans ng-repeat via $index
  • Utiliser la méthode array.splice(from, nbToDelete) pour retirer le film de $scope.movies

Solution

git checkout -f step-5-delete-solution

Exercice 8 : Modifier un film

git checkout -f step-5-edit

Un bouton de modification vient d'être ajouté pour chaque film. Implémentez son action

  • Petite contrainte : Pas de popup. La page d'édition doit être accessible via l'url /#/movies/edit/:id
  • Utiliser un nouveau fichier HTML pour la page d'édition et lui affecter un nouveau controller
  • PUT /server/api/movies
  • GET /server/api/movies/:id
  • Utiliser le service $routeParams dans le controller pour récupérer l'id du film à modifier
  • Utiliser le service $location.path('/path') pour naviguer vers /#/path

Solution

git checkout -f step-5-edit-solution

Récapitulatifs

  • Double Data Binding
    • $scope : seule source de vérité
    • Elimination du code de manipulation du DOM

      (60 à 80% de votre code applicatif)

  • Modèle Vue Contrôleur
    • Modèle = simple object javascript
    • Vue = HTML valide
    • Contrôleur = fonction javascript
  • Single Page Application
    • Navigation client side
    • Agnostique au Back end

Vers des concepts plus avancées

Formulaires & validations

HTML5
HTML5 Inputs
http://www.wufoo.com/html5/
http://www.wufoo.com/html5/

ng-model

input, textarea, select

Data Binding


    <input type="text" ng-model="name">
                    

Validations


    <input type="email" ng-model="user.mail">
                    

    <input ng-model="name"
              required
              ng-minlength="3"
              ng-maxlength="10"
              ng-pattern="[^a-d]"
            >
                    

Etat

  • $pristine
  • $dirty
  • $valid
  • $invalid
  • $error

Validation manuelle

$setValidity(validationErrorKey, isValid)

  • validationErrorKey : nom de la règle de validation
    • required
    • email
    • min
    • maxlength
  • isValid : true ou false

     $setValidity('email', false)       // email invalide
                 

form

Ensemble d'inputs

Etat

  • $pristine
  • $dirty
  • $valid
  • $invalid
  • $error

Nommer vos formulaires et vos champs de saisies avec l'attribut name

Exemple


    <form name="myForm">
        <input name="myInput">
    </form>
                    

    myForm.myInput.$error.required    // état de validité d'une contrainte
    myForm.myInput.$invalid           // état de validité d'un champ de saisie
    myForm.$dirty && myForm.$valid    // vérifier si le formulaire est vierge et valide
                    

Exercice 9 : Valider votre formulaire

Validez le formulaire de création d'un film

  • Le titre du film est requis
  • La note doit être comprise en 1 et 5
  • 
        <div class="control-group error">
            <label class="control-label" >
            <input >
            <span class="help-block">...</span>
        </div>
                            
  • Affectez la classe error si le champ est invalide
    
            <div ng-class="{className : boolean}"></div>
        
  • Afficher le message d'erreur (span help-bloc) si le champ est invalide
    
            <div ng-show="boolean"></div>
        
  • Désactiver un button de manière conditionnelle avec l'attribut ng-disabled
    
             <button ng-disabled="boolean"></button>
         

Solution

git checkout -f step-6

Créer votre propre service

Fournit des tâches spécifiques à l'application

Exemple : $scope, $http

Créer ses propres services


myModuleApp.factory("CountService", function (dep1, dep2, ...) {

    // privée
    var counter = 0;

    // publique
    return {
        increment : function(){
            counter = counter + 1;
        };
    };
});
                        

function myController($scope, CountService){
    CountService.increment();
};
                        

Exercice 10 : Créer votre propre service

Actuellement, nous utilisons le service $http en l'injectant dans les controllers et nous dupliquons à chaque fois l'url de l'API. Améliorons ceci en créant notre propre service pour nous connecter au back end pour les opérations de CRUD sur les films.

  • Appelons ce service Movies
  • Par exemple, dans le contrôleur, au lieu d'appeler $http.get("/server/api/movies"), j'aimerai appeler Movies.fetch();
  • De manière analogue, idem pour les autres opérations CRUD

Solution

git checkout -f step-7

$resource

Objet vous permettant d'intérargir avec un back end RESTful

RESTful Web API

  • GET /server/api/movies
  • GET /server/api/movies/1
  • POST /server/api/movies
  • PUT /server/api/movies/1
  • DELETE /server/api/movies/1
  • GET /server/api/movies/1/actors
  • GET /server/api/movies?category="Horreur"

    $resource(url, paramDefaults, actions);
                    
  • url : template URL avec les paramètres préfixés par :
    • /user/:username
  • paramDefaults : valeur par défault des paramètres. Si le paramètre n'est pas présent dans l'url, alors il est ajouté après ?
    • {username : 'Dupont', order : 'age'}
    • /user/Dupont?order=age
  • actions : objet permettant de paramétré des actions personnalisées
    • 
          {
              action1: {method:?, params:?, isArray:?},
              action2: {method:?, params:?, isArray:?},
              ...
          }
                                          

Actions par défaut


    {
        'get':    {method:'GET'},
        'save':   {method:'POST'},
        'query':  {method:'GET', isArray:true},
        'remove': {method:'DELETE'},
        'delete': {method:'DELETE'}
    };
                    

Ressource retournée

Les ressources récupérées à l'aide de $resource().get() dispose de méthodes $save, $update, $remove, $delete permettant d'effectué les opérations POST, PUT, DELETE


     var Car = $resource('/car/:carId', {carId :'@id'});
     var car = Car.get({carId:123}, function() {
         car.brand = "Toyota";
         car.$save();
     });
                     

Note : La valeur du paramètre peut être préfixé par @. Cela signifie que cette valeur est extraite de l'objet. Utile pour les opérations de type POST, PUT, DELETE

Exercice 11 : Exploiter $resource

Dans le service Movies, remplacer $http par l'utilisation du service $resource. Vous pouvez ainsi gérer tous les appels suivants avec une seule URL paramétrée

  • GET /server/api/movies
  • GET /server/api/movies/1
  • POST /server/api/movies
  • PUT /server/api/movies/1
  • DELETE /server/api/movies/1
  • GET /server/api/movies/1/actors
  • GET /server/api/movies?category="Horreur"

Filtres AngularJS

Formate les données avant de les afficher

hello

HELLO

1368730200

Jeudi 16 mai 2013 20H50

1234.562342

1 234,56 €

[Thierry, David, Nelly, Alex, Claire]

[Alex, Claire, David, Nelly, Thierry]

[pommes, pâtes, farine, PQ]

[PQ]

Transformer la donnée avant de l'afficher à l'utilisateur


$scope.amount = 1234.56;
                   

<div>{{amount}}</div>                     1234.56
                   

<div>{{amount | currency}}</div>           $1,234.56
                   

<div>{{amount | currency:"USD$"}}</div>    USD$1,234.56
                   

Filtres AngularJS

  • currency
  • date
  • lowercase
  • uppercase
  • limitTo
  • filter
  • orderBy

Filtre date

Formate une date selon un certain format et selon une locale


<div>{{uneDate | date:format}}</div>
                    

Ce filtre accepte un format (string) en argument.

Consultez la liste des formats possibles

Exercice 12 : Améliorons la lisibilité de nos données

Actuellement, la date et le prix des films ne sont pas visuellement acceptable.

  • Affichez une date lisible pour l'utilisateur
  • Pour le prix du film, afficher le prix correctement avec sa devise.
  • Mettez les titres des films en majuscules

Filtre filter

Permet de filtrer un sous ensemble d'une collection et retourne ce sous ensemble.


<div>{{collection | filter:predicat}}</div>
                    

Ce filtre accepte un predicat (string) en argument. Toute chaine de caractère ou propriété qui contient ce prédicat sera retournée dans une nouvelle collection.

Exercice 13 : Rechercher un film

git checkout -f step-8

Une barre de recherche est disponible. Implémentez une fonction de recherche en utilisant le filtre filter

  • Tirez partie du data-binding
  • Pas de code nécessaire dans le controller, seulement dans la page HTML

Filtre orderBy

Tri une collection selon un predicat


   <div>{{collection | orderBy:predicat:reverse}}</div>

Ce filtre accepte un predicat (string) en argument. Ce prédicat désigne la propriété sur lequel est fait le tri. Un deuxième argument (boolean) permet d'inverser le sens du tri

Exercice 14 : Trier les films par année et par titre

2 boutons de tri sont disponibles. Utilisez le filtre orderBy pour implémentez les tris correspondants

  • Tirez partie du data-binding
  • Les filtres peuvent être chaînés
  • Pas de code nécessaire dans le controller, seulement dans la page HTML

Solution

git checkout -f step-8-solution

Créer ses propres filtres


angularMovieApp.filter('greet', function () {

    return function(inputValue, arg1, arg2, ....) {

        return 'Hello ' + inputvalue;

    };

});
                        

<div>{{name | greet}}</div>
<div>{{name | greet:arg1:arg2}}</div>
                        

Exercice 15 : Je veux de vraies étoiles

git checkout -f step-9

Créer un filtre permettant d'afficher le nombre d'étoiles correspondant à la note.

  • Voici la caractère unicode pour une étoile : "\u2605"
  • Créez le filtre dans un nouveau fichier filters.js

Exercice 16 : Une image vide par défault

Dans le répertoire img, se trouve une image no-poster.jpg. Créer un filtre qui affiche cette image lorsqu'aucun poster n'est disponible pour un film.

Solution

git checkout -f step-9-solution

Directives AngularJS

Permet de :

Etendre le langage HTML

Créer ses propres composants

Directive ng-switch

Change la structure du DOM de manière conditionnel


<section ng-switch on="value">
    <div ng-switch-when="one">Valeur1</div>
    <span ng-switch-when="two">Valeur2</span>
    <div ng-switch-default >Default</div>
</section>
                    

Exercice 17 : Une liste de films plus compacte

git checkout -f step-10

Vous allez permettre à l'utilisateur de basculer vers une vue plus compacte de la liste des films.

  • Un bouton vient d'être rajouté.
  • Utiliser la directive ng-switch
  • Tirez partie du data-binding
  • Aidez vous du snippet de code de la slide suivante pour une vue en mode tableau

<table class="table table-striped">
    <thead>
    <tr>
        <th>#</th>
        <th>Titre</th>
        <th>Réalisateur</th>
        <th>Année de sortie</th>
        <th>Note</th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="movie in movies | orderBy:tri:reverse | filter : search ">
        <td>{{$index + 1}}</td>
        <td>{{movie.title}}</td>
        <td>{{movie.directors}}</td>
        <td>{{movie.releaseYear}}</td>
        <td>{{movie.rate | stars}}</td>
    </tr>
    </tbody>
</table>
                    

Solution

git checkout -f step-10-solution

Directives vues

  • ng-repeat
  • ng-model
  • ng-controller
  • ng-switch
  • ng-show
  • ng-class
Tabs

    <ul class="nav-tabs">
        <li class="active">
            <a href="#">Primary tab</a>
        </li>
        <li>
            <a href="#">Primary tab</a>
        </li>
        <li>
            <a href="#">Primary tab</a>
        </li>
    </ul>
                    
Tabs

    <tabs>
        <pane title="Primary tab" active>
            <!-- contenu -->
        </pane>
        <pane title="Secondary tab">
            <!-- contenu -->
        </pane>
        <pane title="some other tab">
            <!-- contenu -->
        </pane>
    </tabs>
                    

Libérez votre imagination


        <calendar></calendar>
                    

        <progress-bar></progress-bar>
                    

        <dialog></dialog>
                    

        <datepicker></datepicker>
                    

        <accordion></accordion>
                    

Créer ses propres directives

  • Scope
  • Linking function
  • Compile
  • Restriction
  • Transclusion
WTF !!!

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        restrict : 'E',
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        transclude : false,
        scope : false,
        require : false,
        controller : function($scope, $element, $attrs){...},
        compile : function(tElement, tAttrs, transclude){...},
        link : function (scope, element, attrs){...}
    };
});
                       

Plusieurs types d'invocations possibles


                        <div navbar ></div>
                    

                         <navbar></navbar>
                     

                         <div class="navbar" ></div>
                     

                         <!-- directive : navbar -->
                     

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",









    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",








    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,







    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        restrict : 'EACM',






    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        restrict : 'EACM',
        scope : true,





    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        restrict : 'EACM',
        scope : {
            data1 : '@',
            data2 : '=',
            data3 : '&'
        },





    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        restrict : 'EACM',
        scope : true,
        transclude : true,





    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        restrict : 'EACM',
        scope : true,
        transclude : true,
        compile : function(tElement, tAttrs, transclude){

             return function(scope, iElement, iAttrs, controller){

             }
        }
    };
});
                       

myModuleApp.directive("MaDirective", function (dep1, dep2) {
    return {
        template : "<div></div>",
        templateUrl : "/path/to/ma-directive.html",
        replace : true,
        restrict : 'EACM',
        scope : true,
        transclude : true,
        compile : function(tElement, tAttrs, transclude){

            return function(scope, iElement, iAttrs, controller){

            }
        }
        controller : function($scope, $element, $attrs, $transclude){...}
    };
});
                       

{{ demo }}

Outils autour d'AngularJS

Batarang

Yeoman

Karma (Testacular)

Plugin Intellij / webstorm

AngularUI

AngularStrap

Ressources

Pourquoi AngularJS ?

  • Beaucoup moins de code à écrire grâce au data-binding bi-directionnel
  • Modularisation du code avec les modules et les services injectables : Pas de pollution de l'espace global
  • AngularJS peut être actif sur une portion de la page HTML
  • Créer ses propres composants réutilisable grâce aux directives
  • Développement guidée
  • Framework qui promeut les tests unitaires
  • API bien documentée pour certains aspects
  • Excellent tutorial pour débuter

Difficultés

  • Documentation officielle pauvre sur certaines notions
  • Ecriture des directives pouvant être compliquée si on ne maîtrise pas son cycle de vie
    • Compilation
    • Link
    • Transclusion
    • Restriction
  • Le data-binding bi-directionnel ne fonctionne pas hors du "monde" AngularJS. Il faut utiliser $scope.$watch() et $scope.$apply() pour surveiller et mettre à jour manuellement une valeur
  • Intégration librairies UI jQuery pouvant être problématique
    • Je la wrappe dans une directive ?
    • ... ou je crée une directive ?

Merci de votre attention !

Thierry Lau - @laut3rry