Semilla de AngularJS: poner JavaScript en archivos separados (app.js, controllers.js, directives.js, filtros.js, services.js)

93

Estoy usando la plantilla de semilla angular para estructurar mi aplicación. Inicialmente pongo toda mi código JavaScript en un solo archivo, main.js. Este archivo contenía mi declaración de módulo, controladores, directivas, filtros y servicios. La aplicación funciona bien así, pero me preocupa la escalabilidad y la capacidad de mantenimiento a medida que mi aplicación se vuelve más compleja. Me di cuenta de que la plantilla de semilla angular tiene archivos separados para cada uno de estos, así que intenté distribuir mi código desde el main.jsarchivo único en cada uno de los otros archivos mencionados en el título de esta pregunta y que se encuentran en el app/jsdirectorio de angular -plantilla de semillas .

Mi pregunta es: ¿cómo administro las dependencias para que la aplicación funcione? La documentación existente que se encuentra aquí no es muy clara a este respecto, ya que cada uno de los ejemplos dados muestra un solo archivo fuente de JavaScript.

Un ejemplo de lo que tengo es:

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...
    }
]);

filtros.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
}

¿Cómo gestiono las dependencias?

Gracias por adelantado.

ChrisDevo
fuente
2
hay algunos artículos sobre esto. por ejemplo, briantford.com/blog/huuuuuge-angular-apps.html , y algunos proyectos, como yeoman. Hay dos formas de abordar esto: separar los componentes por capas (como semilla angular) o por característica, como sugieren algunos artículos.
Eduard Gamonal
Cuando declaras un módulo, por ejemplo, angular.module ('myApp.service1', []) , creo que necesitas agregar dependencias al [], por ejemplo, angular.module ('myApp.service1', ['myApp.service2', 'myApp .servicio2 ']). . Soy bastante nuevo en AngularJS, por lo que puedo estar equivocado. Si esto funciona, avíseme y publicaré una respuesta.
Danny Varod
¿Dónde debería insertarse esa línea? Lo puse angular.module('myApp.services', ['myApp.services']);en app.js sin suerte.
ChrisDevo
@ChrisDevo 1. Cuando responda a los comentarios, comience siempre el comentario con '@' y el nombre del usuario (sin espacios), para que el usuario reciba una notificación. 2. myApp.services debe depender de otros módulos, no de sí mismo, por ejemplo angular.module('myApp.services', ['myApp.server', 'myApp.somethingElse']).
Danny Varod
@DannyVarod, ¿editaste tu comentario anterior? Lo leí como angular.module('myApp.service1', ['myApp.service1', 'myApp.service2'])originalmente. De lo contrario, no habría incluido myApp.services como su propia dependencia.
ChrisDevo

Respuestas:

108

El problema se debe a que "redeclara" el módulo de la aplicación en todos los archivos separados.

Así es como se ve la declaración del módulo de la aplicación (no estoy seguro de que la declaración sea el término correcto):

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

Así es como se ve la asignación (tampoco estoy seguro si la asignación es el término correcto) a su módulo de aplicación:

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

Observe la falta de corchetes.

Entonces, la versión anterior, una con corchetes, solo debe usarse una vez, generalmente en su app.jso main.js. Todos los demás archivos asociados - controladores, directivas, filtros … - deben usar la última versión, la que no tiene corchetes.

Espero que tenga sentido. ¡Salud!

Justin L.
fuente
9
He encontrado una solución a este problema. Parece que solo se necesita un módulo myApp. En mi archivo app.js creé una var para él.En var myApp = angular.module('myApp', []);todos los demás archivos, simplemente me referí a esta var (por ejemplo, myApp.filter('myFilter', function(MyService) { /* filter code */ });siempre que agregué los nombres de archivo en las etiquetas de script en mi html, no necesitaba declarar ninguna otra dependencias. La plantilla de proyecto de semilla angular es bastante confusa en este sentido.
ChrisDevo
1
Ahora estoy realmente confundido. He vuelto y quitó la var mundial myApp, por lo que ahora es un módulo anónimo en app.js: angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers']);. En todos los demás archivos también declaré módulos anónimos como este angular.module('myApp.filter', []).filter('myFilter', function(MyService) { /* filter code */ });. Mi aplicación ahora también funciona así. Revisé mi historial de código y no puedo ver dónde es diferente a lo que yo cuando no funcionó.
ChrisDevo
4
Te prometo que esto funciona. Lo uso todos los días desarrollando aplicaciones angulares. Nuevamente, tengo una declaración de angular.module('myApp', []);(observe los corchetes) en mi archivo js principal. Luego, todos los demás archivos se refieren al módulo mediante la sintaxis angular.module('myApp').controllero .directive.
Justin L.
5
Los errores se deben a que Angular está buscando módulos llamados myApp.controller, myApp.filters… pero esos no son módulos, son componentes que extienden el único módulo llamado myApp. Todo debería funcionar si elimina todas sus myApp.whatever de las dependencias de su módulo. Simplemente mantenga los corchetes vacíos (a menos que incluya otros módulos angulares verdaderos, por ejemplo ngCookies, ngResource) cuando declare el módulo principal de su aplicación:angular.module('myApp', []);
Justin L.
1
Ah bien. Al colocarlos como una dependencia para el módulo de su aplicación, se producirá un error porque tiene que encontrarlos. Si los saca de los corchetes, puede cargarlos a voluntad y a su aplicación no le importará. ngResourcees definitivamente uno de los que coloca en los corchetes de declaración de su módulo. Si mi respuesta ayudó, se agradecería mucho votar y aprobarla.
Justin L.
12

Si desea colocar las diferentes partes de su aplicación ( filters, services, controllers) en diferentes archivos físicos, hay dos cosas que debe hacer:

  1. Declare esos espacios de nombres (a falta de un término mejor) en su app.js o en cada archivo;
  2. Consulte esos espacios de nombres en cada uno de los archivos.

Entonces, tu app.jsse vería así:

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

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

Y en cada archivo individual:

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...
});

Todo esto se puede combinar en una sola llamada:

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

La parte importante es que no debes redefinir angular.module('myApp'); eso haría que se sobrescribiera al crear una instancia de sus controladores, probablemente no sea lo que desea.

George Stocker
fuente
1
Me gusta este enfoque, puede usarlo para crear una estructura escalonada (digamos que su controlador necesita un servicio o filtro). Tenga en cuenta que también descubrí que una vez que se inyecta una subdependencia (como ese filtro para un controlador), también está disponible en todos los padres anteriores (si se visualiza como un árbol) sin volver a declarar la inyección, simplemente no t olvídelo si cambias el hijo y lo estás usando en el padre. También recomendaría nunca poner más de un módulo (espacio de nombres) en un archivo y nunca intentar usar el mismo espacio de nombres en varios archivos.
Gary
¿Qué sucede si tengo dos archivos de controlador o dos archivos de servicios? ¿La instanciación se produciría para cada archivo individual?
Avinash Raj
3

Obtiene el error porque aún no myApp.servicesha definido . Lo que hice hasta ahora fue poner todas las definiciones iniciales en un archivo y luego usarlas en otro. Como para tu ejemplo, pondría:

app.js

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

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

Eso debería eliminar el error, aunque creo que debería haber leído el artículo que Eduard Gamonal mencionó en uno de los comentarios.

Torsten Engelbrecht
fuente
Gracias, Torsten, pero leí ese artículo y agregué la línea angular.module('myApp.services', []);a mi archivo app.js. Ninguno aborda realmente mi problema.
ChrisDevo