Semente do AngularJS: colocar o JavaScript em arquivos separados (app.js, controllers.js, directives.js, filters.js, services.js)

93

Estou usando o template angular-seed para estruturar meu aplicativo. Inicialmente, coloquei todo o meu código JavaScript em um único arquivo main.js,. Este arquivo continha minha declaração de módulo, controladores, diretivas, filtros e serviços. O aplicativo funciona bem assim, mas estou preocupado com a escalabilidade e manutenção, pois meu aplicativo se torna mais complexo. Percebi que o modelo angular-seed tem arquivos separados para cada um deles, então tentei distribuir meu código de um único main.jsarquivo em cada um dos outros arquivos mencionados no título para esta questão e encontrados no app/jsdiretório do angular modelo de sementes .

Minha pergunta é: como faço para gerenciar as dependências para fazer o aplicativo funcionar? A documentação existente encontrada aqui não é muito clara a esse respeito, pois cada um dos exemplos fornecidos mostra um único arquivo-fonte JavaScript.

Um exemplo do que tenho é:

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
}

Como faço para gerenciar as dependências?

Desde já, obrigado.

ChrisDevo
fonte
2
existem alguns artigos sobre isso. por exemplo, briantford.com/blog/huuuuuge-angular-apps.html e alguns projetos, como o yeoman. Existem duas maneiras de abordar isso: separando os componentes por camadas (como semente angular) ou por recurso, como alguns artigos sugerem.
Eduard Gamonal
Quando você declara um módulo, por exemplo, angular.module ('myApp.service1', []) , acho que você precisa adicionar dependências ao [] por exemplo, angular.module ('myApp.service1', ['myApp.service2', 'myApp .service2 ']). . Sou muito novo no AngularJS, então posso estar errado. Se der certo, me avise e postarei uma resposta.
Danny Varod
Onde essa linha deve ser inserida? Eu coloquei angular.module('myApp.services', ['myApp.services']);no app.js sem sorte.
ChrisDevo
@ChrisDevo 1. Ao responder a comentários, sempre inicie o comentário com '@' e o nome do usuário (sem espaços), para que o usuário receba uma notificação. 2. myApp.services deve ser dependente de outros módulos, não de si mesmo, por exemplo angular.module('myApp.services', ['myApp.server', 'myApp.somethingElse']).
Danny Varod
@DannyVarod, você editou seu comentário anterior? Eu li como angular.module('myApp.service1', ['myApp.service1', 'myApp.service2'])originalmente. Caso contrário, eu não teria incluído myApp.services como sua própria dependência.
ChrisDevo

Respostas:

108

O problema é causado por você "redeclarar" o módulo do aplicativo em todos os seus arquivos separados.

Esta é a aparência da declaração do módulo do app (declaração não certa é o termo correto):

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

Esta é a aparência de atribuição (não tenho certeza se atribuição é o termo correto) para seu módulo de aplicativo:

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

Observe a falta de colchetes.

Portanto, a versão anterior, aquela com colchetes, deve ser usada apenas uma vez, geralmente em seu app.jsou main.js. Todos os outros arquivos associados - controladores, diretivas, filtros ... - devem usar a última versão, aquela sem os colchetes.

Espero que faça sentido. Felicidades!

Justin L.
fonte
9
Eu encontrei uma solução para este problema. Parece que apenas um módulo é necessário myApp. No meu arquivo app.js, criei uma var para ele. var myApp = angular.module('myApp', []);Em todos os outros arquivos, simplesmente me referi a esta var (por exemplo, myApp.filter('myFilter', function(MyService) { /* filter code */ });desde que eu tenha adicionado os nomes dos arquivos nas tags de script no meu html, não precisei declarar nenhum outro dependências. O modelo de projeto angular-seed é bastante confuso a este respeito.
ChrisDevo
1
Agora estou realmente confuso. Eu tenho ido para trás e retirou a var mundial myApp, de modo que agora é um módulo anônimo em app.js: angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers']);. Em todos os outros arquivos também declarei módulos anônimos como este angular.module('myApp.filter', []).filter('myFilter', function(MyService) { /* filter code */ });. Meu aplicativo agora também funciona assim. Eu examinei meu histórico de código e não consigo ver onde isso é diferente do que quando não funcionava.
ChrisDevo
4
Eu prometo a você que isso funciona. Eu uso todos os dias desenvolvendo aplicativos angulares. Novamente, eu tenho uma declaração de angular.module('myApp', []);(observe os colchetes) em meu arquivo js principal. Então, todos os outros arquivos se referem ao módulo usando a sintaxe angular.module('myApp').controllerou .directive.
Justin L.
5
Os erros são causados ​​porque o Angular está procurando por módulos chamados myApp.controller, myApp.filters ... mas, esses não são módulos, são componentes que estendem um módulo chamado myApp. Tudo deve funcionar se você remover todo o seu myApp.whatever das dependências do módulo. Basta manter os colchetes vazio (a menos que você está incluindo outros módulos angulares verdadeiras, por exemplo ngCookies, ngResource) quando declarar o seu módulo principal app:angular.module('myApp', []);
Justin L.
1
Ah ok. Ao colocar aqueles como uma dependência para o módulo aplicativo, ele irá erro porque ele tem que encontrá-los. Se você retirá-los dos colchetes, poderá carregá-los à vontade e seu aplicativo não ligará. ngResourceé definitivamente um daqueles que você coloca nos colchetes de declaração do módulo. Se minha resposta ajudasse, votar e aprovar seria muito apreciado.
Justin L.
12

Se você deseja colocar diferentes partes do seu aplicativo ( filters, services, controllers) em arquivos físicos diferentes, há duas coisas que você deve fazer:

  1. Declare esses namespaces (por falta de um termo melhor) em seu app.jsou em cada arquivo;
  2. Consulte esses namespaces em cada um dos arquivos.

Então, você app.jsficaria assim:

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

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

E em cada arquivo 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...
});

Tudo isso pode ser combinado em uma chamada:

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

O importante é que você não deve redefinir angular.module('myApp'); isso faria com que fosse sobrescrito quando você instanciar seus controladores, provavelmente não o que você deseja.

George Stocker
fonte
1
Eu gosto dessa abordagem, você pode usá-la para criar uma estrutura em camadas (digamos que seu controlador precisa de um serviço ou filtro). Note, eu também descobri que uma vez que uma subdependência (como aquele filtro para um controlador) é injetada, ela também está disponível em todos os pais acima (se visualizando como uma árvore) sem, redeclarar a injeção, apenas don ' Não se esqueça disso se mudar de filho e estiver usando no pai. Além disso, eu recomendaria nunca colocar mais de um módulo (namespace) em um arquivo e nunca tente usar o mesmo namespace em vários arquivos.
Gary
E se eu tiver dois arquivos de controlador ou dois arquivos de serviços? A instanciação aconteceria para cada arquivo?
Avinash Raj,
3

Você obtém o erro porque ainda não definiu myApp.services. O que fiz até agora foi colocar todas as definições iniciais em um arquivo e depois usá-las em outro. Como para o seu exemplo, eu colocaria:

app.js

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

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

Isso deve eliminar o erro, embora eu ache que você deva ler o artigo Eduard Gamonal mencionado em um dos comentários.

Torsten Engelbrecht
fonte
Obrigado, Torsten, mas li esse artigo e adicionei a linha angular.module('myApp.services', []);ao meu arquivo app.js. Nenhum dos dois realmente resolve meu problema.
ChrisDevo