AngularJS seed: mettre JavaScript dans des fichiers séparés (app.js, controllers.js, directives.js, filters.js, services.js)

93

J'utilise le modèle angular-seed pour structurer mon application. Dans un premier temps, je mets tout mon code JavaScript dans un seul fichier, main.js. Ce fichier contenait ma déclaration de module, les contrôleurs, les directives, les filtres et les services. L'application fonctionne bien comme ça, mais je m'inquiète de l'évolutivité et de la maintenabilité car mon application devient plus complexe. J'ai remarqué que le modèle angular-seed a des fichiers séparés pour chacun d'eux, j'ai donc tenté de distribuer mon code à partir du main.jsfichier unique dans chacun des autres fichiers mentionnés dans le titre de cette question et trouvés dans le app/jsrépertoire de l' angular -modèle de graine .

Ma question est la suivante: comment gérer les dépendances pour que l'application fonctionne? La documentation existante trouvée ici n'est pas très claire à cet égard puisque chacun des exemples donnés montre un seul fichier source JavaScript.

Un exemple de ce que j'ai est:

app.js

angular.module('myApp', 
    ['myApp.filters',
     'myApp.services',
     'myApp.controllers']);

controllers.js

angular.module('myApp.controllers', []).
    controller('AppCtrl', [function ($scope, $http, $filter, MyService) {

        $scope.myService = MyService; // found in services.js

        // other functions...
    }
]);

filters.js

angular.module('myApp.filters', []).
    filter('myFilter', [function (MyService) {
        return function(value) {
            if (MyService.data) { // test to ensure service is loaded
                for (var i = 0; i < MyService.data.length; i++) {
                    // code to return appropriate value from MyService
                }
            }
        }
    }]
);

services.js

angular.module('myApp.services', []).
    factory('MyService', function($http) {
        var MyService = {};
        $http.get('resources/data.json').success(function(response) {
            MyService.data = response;
        });
        return MyService;
    }
);

main.js

/* This is the single file I want to separate into the others */
var myApp = angular.module('myApp'), []);

myApp.factory('MyService', function($http) {
    // same code as in services.js
}

myApp.filter('myFilter', function(MyService) {
    // same code as in filters.js
}

function AppCtrl ($scope, $http, $filter, MyService) {
    // same code as in app.js
}

Comment gérer les dépendances?

Merci d'avance.

ChrisDevo
la source
2
il y a quelques articles à ce sujet. par exemple, briantford.com/blog/huuuuuge-angular-apps.html , et certains projets, comme yeoman. il existe deux manières d'aborder cela: séparer les composants par couches (comme angular-seed) ou par caractéristique, comme le suggèrent certains articles.
Eduard Gamonal
Lorsque vous déclarez un module, par exemple angular.module ('myApp.service1', []), je pense que vous devez ajouter des dépendances au [] par exemple angular.module ('myApp.service1', ['myApp.service2', 'myApp .service2 ']). . Je suis assez nouveau sur AngularJS, donc je me trompe peut-être. Si cela fonctionne, faites-le moi savoir et je posterai une réponse.
Danny Varod
Où cette ligne doit-elle être insérée? J'ai mis angular.module('myApp.services', ['myApp.services']);dans app.js sans succès.
ChrisDevo
@ChrisDevo 1. Lorsque vous répondez aux commentaires, commencez toujours le commentaire par «@» et le nom de l'utilisateur (sans espaces), afin que l'utilisateur reçoive une notification. 2. myApp.services doit dépendre d'autres modules, pas de lui-même, par exemple angular.module('myApp.services', ['myApp.server', 'myApp.somethingElse']).
Danny Varod
@DannyVarod, avez-vous édité votre commentaire précédent? Je l'ai lu comme à l' angular.module('myApp.service1', ['myApp.service1', 'myApp.service2'])origine. Sinon, je n'aurais pas inclus myApp.services comme sa propre dépendance.
ChrisDevo

Réponses:

108

Le problème est dû au fait que vous "redéclarez" votre module d'application dans tous vos fichiers séparés.

Voici à quoi ressemble la déclaration du module d'application (pas sûr que la déclaration soit le bon terme):

angular.module('myApp', []).controller( //...

Voici à quoi ressemble l'affectation (pas sûr que l'affectation soit le bon terme non plus) à votre module d'application:

angular.module('myApp').controller( //...

Remarquez l'absence de crochets.

Ainsi, l'ancienne version, une avec les crochets, ne doit être utilisée qu'une seule fois, généralement dans votre fichier app.jsou main.js. Tous les autres fichiers associés - contrôleurs, directives, filtres … - doivent utiliser la dernière version, celle sans les crochets.

J'espère que cela à du sens. À votre santé!

Justin L.
la source
9
J'ai trouvé une solution à ce problème. Il semble qu'un seul module soit nécessaire myApp. Dans mon fichier app.js, j'ai créé une variable pour cela var myApp = angular.module('myApp', []);Dans tous les autres fichiers, je me suis simplement référé à cette myApp.filter('myFilter', function(MyService) { /* filter code */ });variable (par exemple, tant que j'ai ajouté les noms de fichier dans les balises de script dans mon html, je n'avais pas besoin de déclarer d'autres dépendances. Le modèle de projet angular-seed est assez déroutant à cet égard.
ChrisDevo
1
Maintenant, je suis vraiment confus. Je suis retourné et enlevé le var global myApp, de sorte qu'il est maintenant un module anonyme app.js: angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers']);. Dans tous les autres fichiers, j'ai également déclaré des modules anonymes comme celui-ci angular.module('myApp.filter', []).filter('myFilter', function(MyService) { /* filter code */ });. Mon application fonctionne maintenant aussi comme ça. J'ai parcouru mon historique de code et je ne peux pas voir en quoi cela est différent de ce que j'ai quand cela n'a pas fonctionné.
ChrisDevo
4
Je vous promets que cela fonctionne. Je l'utilise tous les jours pour développer des applications angulaires. Encore une fois, j'ai une déclaration de angular.module('myApp', []);(notez les crochets) dans mon fichier js principal. Ensuite, tous les autres fichiers font référence au module en utilisant la syntaxe angular.module('myApp').controllerou .directive.
Justin L.
5
Les erreurs sont causées parce qu'Angular recherche des modules appelés myApp.controller, myApp.filters… mais, ce ne sont pas des modules, ce sont des composants étendant le module appelé myApp. Tout devrait fonctionner si vous supprimez tous vos myApp.wwhat de vos dépendances de module. Il suffit de garder les crochets vides (sauf si vous incluez d' autres véritables modules angulaires, par exemple ngCookies, ngResource) lors de la déclaration de votre module principal de l' application:angular.module('myApp', []);
Justin L.
1
Ah ok. En les plaçant en tant que dépendance pour votre module d'application, cela entraînera une erreur car il doit les trouver. Si vous les supprimez des crochets, vous pouvez les charger à volonté et votre application s'en fiche. ngResourceest certainement l'un de ceux que vous placez dans les crochets de déclaration de votre module. Si ma réponse vous a aidé, voter et approuver cela serait très apprécié.
Justin L.
12

Si vous souhaitez placer vos différentes parties de votre application ( filters, services, controllers) dans différents fichiers physiques, vous devez faire deux choses:

  1. Déclarez ces espaces de noms (faute d'un meilleur terme) dans votre app.jsou dans chaque fichier;
  2. Reportez-vous à ces espaces de noms dans chacun des fichiers.

Donc, vous app.jsressembleriez à ceci:

angular.module('myApp', ['external-dependency-1', 'myApp.services', 'myApp.controllers'])
.run(function() {
   //...

})
.config(function() {
  //...
});

Et dans chaque dossier individuel:

services.js

angular.module('myApp.services', []); //instantiates
angular.module('myApp.services') //gets
.factory('MyService', function() { 
  return {};
});

controllers.js

angular.module('myApp.controllers', []); //instantiates
angular.module('myApp.controllers')      //gets
.controller('MyCtrl', function($scope) {
  //snip...
})
.controller('AccountCtrl', function($scope) {
  //snip...
});

Tout cela peut être combiné en un seul appel:

controllers.js
angular.module('myApp.controllers', []) 
.controller('MyCtrl', function($scope) {
 //snip...
});    

L'important est que vous ne devriez pas redéfinir angular.module('myApp'); cela entraînerait son écrasement lorsque vous instanciez vos contrôleurs, probablement pas ce que vous voulez.

George Stocker
la source
1
J'aime cette approche, vous pouvez l'utiliser pour créer une structure à plusieurs niveaux (par exemple, votre contrôleur a besoin d'un service ou d'un filtre). Remarque, j'ai également constaté qu'une fois qu'une sous-dépendance (comme ce filtre pour un contrôleur) est injectée, elle est également disponible dans tous les parents ci-dessus (si vous visualisez comme un arbre) sans, re-déclarer l'injection, il suffit de ne pas. t oublier si vous changez l'enfant et que vous l'utilisez dans le parent. De plus, je recommanderais de ne jamais mettre plus d'un module (espace de noms) dans un fichier et de ne jamais essayer d'utiliser le même espace de noms dans plusieurs fichiers.
Gary
Que faire si j'ai deux fichiers de contrôleur ou deux fichiers de services? L'instanciation se produirait-elle pour chaque fichier unique?
Avinash Raj
3

Vous obtenez l'erreur car vous n'avez pas myApp.servicesencore défini . Ce que j'ai fait jusqu'à présent, c'est de mettre toutes les définitions initiales dans un fichier, puis de les utiliser dans un autre. Comme pour votre exemple, je mettrais:

app.js

angular.module('myApp.services', []);

angular.module('myApp', 
    ['myApp.services',
      ...]);

Cela devrait éliminer l'erreur, même si je pense que vous devriez lire l'article Eduard Gamonal mentionné dans l'un des commentaires.

Torsten Engelbrecht
la source
Merci, Torsten, mais j'ai lu cet article et ajouté la ligne angular.module('myApp.services', []);à mon fichier app.js. Ni l'un ni l'autre ne résout vraiment mon problème.
ChrisDevo