Angular, organisation de projet

Bon, notre projet devient vite un peu “pĂ©nible” Ă  maintenir d’un point de vue “javascript”, tout est dans main.js. Nous allons donc “splitter” ça en plusieurs fichiers. Nous n’allons pas utiliser Require.js (soit dit en pensant, vu les possibilitĂ© de modularisation d’Angular, je ne suis pas convaincu du tout d’avoir besoin de Require). Alors, la structure que je vais vous proposer n’est pas parfaite, il y a d’autre moyens de faire, Ă  vous de dĂ©cliner en fonction de vos rĂšgles projets, sensibilitĂ©s, habitudes, etc. 


Une piste serait d’organiser d’une maniùre plus “fonctionnelle”, mais c’est un autre sujet (que j’aborderais certainement).

Pré-requis: avoir lu les articles précédents:

Préparation

Apportons quelques modifications à l’arborescence de notre projet:

Dans le répertoire js, nous allons créer un répertoire controllers avec 4 fichiers javascript:

js/ 
 └── controllers/
     ├── ActualitesCtrl.js
     ├── AdditionCtrl.js       
     ├── CoupsDeCoeurCtrl.js
     └── MainCtrl.js   

Ensuite, toujours dans le répertoire js, nous allons créer un répertoire factories avec 2 fichiers javascript:

   js/    
    └── factories/ 
        ├── Book.js
        └── Models.js                     

Nous devrions donc avoir la structure suivante:

votre_projet/
├── public/
|   ├── bower_components/ /* angular est par ici */
|   ├── js/ 
|   |    ├── controllers/
|   |    |   ├── ActualitesCtrl.js
|   |    |   ├── AdditionCtrl.js       
|   |    |   ├── CoupsDeCoeurCtrl.js
|   |    |   └── MainCtrl.js      
|   |    ├── factories/ 
|   |    |   ├── Book.js
|   |    |   └── Models.js                     
|   |    └── main.js
|   ├── books/ 
|   |    ├── bookForm.html
|   |    └── booksList.html  
|   ├── templates/  
|   |    ├── actualites.html     
|   |    ├── addition.html
|   |    └── coupsdecoeur.html         
|   └── index.html
├── bower.json
└── .bowerrc

Refactoring, c’est parti!

Maintenant nous allons passer dans chacun des nouveaux fichiers.

Models.js

Nous allons extraire le code concernant les modĂšles dans main.js pour le “transfĂ©rer” dans Model.js.

Avant (dans main.js):

booksApp.factory("Models", function() {
  var levels = function() {
    return [
      "", "trÚs bon", "bon", "débutant"
    ];
  }

  return {
    levels : levels
  }
});

AprĂšs (dans /factories/Models.js):

La modification n’est pas Ă©norme.

(function () {

  var Models = function () {
    var levels = function() {
      return [
        "", "trÚs bon", "bon", "débutant"
      ];
    }

    return {
      levels : levels
    }
  };

  //Models.$inject = []; pas d'injection

  angular.module("booksApp").factory("Models", Models);

}());

Book.js

Nous allons extraire le code dans main.js pour le “transfĂ©rer” dans Book.js, sur le mĂȘme principe que prĂ©cĂ©demment:

Avant (dans main.js):

booksApp.factory("Book", function($resource) {
  return $resource("/books/:id", {id: '@id'},{
    update: { method: 'PUT' , params: {id: '@id'}, isArray: false }
  });
})

AprĂšs (dans /factories/Book.js):

  • Il faut bien penser Ă  encapsuler le code dans (function () { //foo }());
  • Attention, il faut passer la dĂ©pendance : $resource, alors Book.$inject = ["$resource"] n’est pas forcĂ©ment obligatoire, mais si vous minifiez votre code, ça peut vous rendre service
  • Et dĂ©clarer notre factory Book dans le module principal : angular.module("booksApp").factory("Book", Book);
(function () {

  var Book = function ($resource) {
    return $resource("/books/:id", {id: '@id'},{
      update: { method: 'PUT' , params: {id: '@id'}, isArray: false }
    });
  };

  Book.$inject = ["$resource"];

  angular.module("booksApp").factory("Book", Book);

}());

Nous allons utiliser le mĂȘme principe pour les contrĂŽleurs :

ActualitesCtrl.js

AprĂšs (dans /controllers/ActualitesCtrl.js):

(function () {

  var ActualitesCtrl = function ($scope) {
    $scope.title = "Actualités";
    $scope.news = [
      { title:"Un nouveau Stephen King Ă  venir?" }
      , { title:"Le Kindle paperwhite est juste une turie" }
      , { title:"Le format epub 3 en détail" }
    ];
  };

  ActualitesCtrl.$inject = ["$scope"];

  angular.module("booksApp").controller("ActualitesCtrl", ActualitesCtrl);

}());

CoupsDeCoeurCtrl.js

AprĂšs (dans /factories/CoupsDeCoeurCtrl.js):

(function () {
  var CoupsDeCoeurCtrl = function ($scope) {
    $scope.title = "Coups de Coeur";
    $scope.items = [
      { title:"Game of Thrones" }
      , { title:"Bilbo le Hobbit" }
      , { title:"Le sorcier de TerreMĂšre" }
    ];
  };

  CoupsDeCoeurCtrl.$inject = ["$scope"];

  angular.module("booksApp").controller("CoupsDeCoeurCtrl", CoupsDeCoeurCtrl);
}());

AdditionCtrl.js

AprĂšs (dans /factories/AdditionCtrl.js):

(function () {

  var AdditionCtrl = function ($scope, $routeParams) {
    $scope.result = parseInt($routeParams['a']) + parseInt($routeParams['b']);
  };

  AdditionCtrl.$inject = ["$scope", "$routeParams"];

  angular.module("booksApp").controller("AdditionCtrl", AdditionCtrl);

}());

Et enfin :

MainCtrl.js

AprĂšs (dans /factories/MainCtrl.js):

(function () {

  var MainCtrl = function ($scope, Models, Book) {

    $scope.whenFormIsLoaded = function() { console.log("Form is loaded ..."); }
    $scope.whenListIsLoaded = function() { console.log("List is loaded ..."); }

    $scope.books = null;
    $scope.levels = Models.levels();

    $scope.selectBook = function(book) {
      $scope.book = book;
    }

    $scope.getAllBooks = function() {
      Book.query().$promise.then(
        function(data) {
          $scope.books = data;
        },
        function(error) {
          console.log("getAllBooks", error);
        }
      )
    }

    $scope.saveBook = function(book) {
      book._id !== undefined ? $scope.updateBook(book) : $scope.createBook(book);
    }

    $scope.createBook = function(book) {
      Book.save(book).$promise.then(
        function(data) {
          $scope.getAllBooks();
          $scope.newBook();
        },
        function(error) {
          console.log("createBook", error);
        }
      )
    }

    $scope.newBook = function() {
      $scope.book = {};
    }

    $scope.updateBook = function(book) {
      Book.update({id: book._id}, book).$promise.then(
        function(data) {
          $scope.newBook();
          $scope.getAllBooks();
        },
        function(error) {
          console.log("updateBook", error);
        }
      )
    }

    $scope.deleteBook = function(id) {
      Book.delete({id: id}).$promise.then(
        function(data) {
          $scope.getAllBooks();
          $scope.newBook();
        },
        function(error) {
          console.log("book delete ERROR", error);
        }
      )
    }


    $scope.getBook = function(id) {
      Book.get({id:id}).$promise.then(
        function(data) {
          console.log(data);
          $scope.book = data;
        },
        function(error) {
          console.log("getBook", error);
        }
      )
    }

    $scope.getAllBooks()
  };

  MainCtrl.$inject = ["$scope", "Models", "Book"];

  angular.module("booksApp").controller("MainCtrl", MainCtrl);

}());

Encore quelques modifications

main.js

Là aussi utilisons le module “pattern”. Maintenant, votre code doit ressembler à ceci:

(function () {
  var booksApp = angular.module("booksApp", ["ngResource", "ngRoute"]);

  booksApp.config(['$routeProvider',
    function($routeProvider) {

      $routeProvider.
        when('/actualites', {
          templateUrl: "templates/actualites.html",
          controller: "ActualitesCtrl"
        })
        .when('/coupsdecoeur', {
          templateUrl: "templates/coupsdecoeur.html",
          controller: "CoupsDeCoeurCtrl"
        })
        .when('/addition/:a/:b', {
          templateUrl: "templates/addition.html",
          controller: "AdditionCtrl"
        })
        .otherwise({
          redirectTo: "/"
        })

    }
  ]);

  booksApp.filter("stars", function() {
    return function(data) {
      switch (data) {
        case "trĂšs bon":
          return "***";
          break;
        case "bon":
          return "**";
          break;
        case "débutant":
          return "*";
          break;
        default:
          return "";
      }
    }
  });

})();

Bien sĂ»r, rien ne vous empĂȘche d’externaliser filter et config

Et finalement:

index.html

Il ne faut bien sĂ»r pas oublier de rĂ©fĂ©rencer tous nos scripts: (il existe des solutions simples pour faciliter la gestion de ces rĂ©fĂ©rences mais c’est un autre sujet).

<script src="js/main.js"></script>

<script src="js/factories/Models.js"></script>
<script src="js/factories/Book.js"></script>

<script src="js/controllers/AdditionCtrl.js"></script>
<script src="js/controllers/ActualitesCtrl.js"></script>
<script src="js/controllers/CoupsDeCoeurCtrl.js"></script>

<script src="js/controllers/MainCtrl.js"></script>

ET hop, c’est fini! Vous pouvez tout relancer, cela fonctionne comme avant et votre projet est plus facile à maintenir.

Bon pont!

blog comments powered by Disqus

Related posts