AngularJS seed: размещение JavaScript в отдельных файлах (app.js, controllers.js, directives.js, filters.js, services.js)

93

Я использую шаблон angular-seed для структурирования своего приложения. Изначально я поместил весь свой код JavaScript в один файл main.js. Этот файл содержал мое объявление модуля, контроллеры, директивы, фильтры и службы. Приложение работает нормально, как это, но я беспокоюсь о масштабируемости и ремонтопригодности, поскольку мое приложение становится более сложным. Я заметил, что в шаблоне angular-seed есть отдельные файлы для каждого из них, поэтому я попытался распространить свой код из одного main.jsфайла в каждый из других файлов, упомянутых в заголовке этого вопроса и найденных в app/jsкаталоге angular -семейный шаблон.

У меня вопрос: как мне управлять зависимостями, чтобы приложение работало? Существующая документация, найденная здесь , не очень ясна в этом отношении, поскольку каждый из приведенных примеров показывает один исходный файл JavaScript.

Пример того, что у меня есть:

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
}

Как мне управлять зависимостями?

Заранее спасибо.

ChrisDevo
источник
2
есть несколько статей об этом. например, briantford.com/blog/huuuuuge-angular-apps.html , и некоторые проекты, такие как yeoman. есть два подхода к этому: разделение компонентов по слоям (например, angular-seed) или по функциям, как предлагают некоторые статьи.
Эдуард Гамонал
Когда вы объявляете модуль, например angular.module ('myApp.service1', []), я думаю, вам нужно добавить зависимости в [], например, angular.module ('myApp.service1', ['myApp.service2', 'myApp .service2 ']). . Я новичок в AngularJS, поэтому могу ошибаться. Если это сработает, дайте мне знать, и я отправлю ответ.
Дэнни Варод
Куда нужно вставить эту строку? Я angular.module('myApp.services', ['myApp.services']);безуспешно вставил в app.js.
ChrisDevo
@ChrisDevo 1. Когда вы отвечаете на комментарии, всегда начинайте комментарий с '@' и имени пользователя (без пробелов), чтобы пользователь получил уведомление. 2. myApp.services должен зависеть от других модулей, а не от себя, например angular.module('myApp.services', ['myApp.server', 'myApp.somethingElse']).
Дэнни Варод
@DannyVarod, вы редактировали свой предыдущий комментарий? Прочитал как angular.module('myApp.service1', ['myApp.service1', 'myApp.service2'])изначально. В противном случае я бы не включил myApp.services как отдельную зависимость.
ChrisDevo

Ответы:

108

Проблема вызвана тем, что вы «повторно объявили» модуль приложения во всех ваших отдельных файлах.

Вот как выглядит объявление модуля приложения (не уверен, что это правильный термин):

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

Вот как выглядит присвоение (не уверен, что это правильный термин) для вашего прикладного модуля:

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

Обратите внимание на отсутствие квадратных скобок.

Таким образом, предыдущая версия с квадратными скобками должна использоваться только один раз, обычно в вашем app.jsили main.js. Все остальные связанные файлы - контроллеры, директивы, фильтры … - должны использовать последнюю версию, без квадратных скобок.

Я надеюсь, что в этом есть смысл. Ура!

Джастин Л.
источник
9
Я нашел решение этой проблемы. Вроде нужен всего один модуль myApp. В моем файле app.js я создал для него переменную. var myApp = angular.module('myApp', []);Во всех других файлах я просто ссылался на эту переменную (например, myApp.filter('myFilter', function(MyService) { /* filter code */ });пока я добавлял имена файлов в теги скрипта в моем html, мне не нужно было объявлять какие-либо другие В этом отношении шаблон проекта angular-seed довольно запутан
ChrisDevo
1
Теперь я действительно в замешательстве. Я пошел обратно и удалить глобальные переменные myApp, так что теперь анонимный модуль app.js: angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers']);. Во всех других файлах я также объявил такие анонимные модули angular.module('myApp.filter', []).filter('myFilter', function(MyService) { /* filter code */ });. Мое приложение теперь тоже работает так. Я просмотрел историю своего кода и не вижу, где это отличается от того, что было у меня, когда это не работало.
ChrisDevo
4
Обещаю, это сработает. Я использую его каждый день, разрабатывая приложения Angular. Опять же, у меня есть одно объявление angular.module('myApp', []);(обратите внимание на квадратные скобки) в моем основном файле js. Затем все остальные файлы ссылаются на модуль с помощью синтаксиса angular.module('myApp').controllerили .directive.
Джастин Л.
5
Ошибки вызваны тем, что Angular ищет модули с именами myApp.controller, myApp.filters ... но это не модули, это компоненты, расширяющие один модуль, называемый myApp. Все должно работать, если вы удалите все myApp.w независимо от зависимостей вашего модуля. Просто держать квадратные скобки пустыми (если вы не включая другие истинные угловые модули, например ngCookies, ngResource) при объявлении вашего основного модуля приложения:angular.module('myApp', []);
Justin L.
1
Ах хорошо. Помещая их в качестве зависимости для вашего модуля приложения, он приведет к ошибке, потому что он должен их найти. Если вы удалите их из квадратных скобок, вы можете загружать их по своему желанию, и вашему приложению все равно. ngResourceопределенно один из тех, которые вы помещаете в скобки объявления модуля. Если бы мой ответ действительно помог, голосование и одобрение были бы очень признательны.
Джастин Л.
12

Если вы хотите поместить различные части приложения ( filters, services, controllers) в разные физические файлы, вам нужно сделать две вещи:

  1. Объявите эти пространства имен (из-за отсутствия лучшего термина) в вашем app.jsили в каждом файле;
  2. Обратитесь к этим пространствам имен в каждом из файлов.

Итак, ваш app.jsбудет выглядеть так:

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

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

И в каждом отдельном файле:

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

Все это можно объединить в один звонок:

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

Важная часть состоит в том, что вы не должны переопределять angular.module('myApp'); это приведет к его перезаписи при создании экземпляров контроллеров, возможно, не того, что вы хотите.

Джордж Стокер
источник
1
Мне нравится этот подход, вы можете использовать его для создания многоуровневой структуры (скажем, вашему контроллеру нужен сервис или фильтр). Обратите внимание, я также обнаружил, что после того, как субзависимость (например, этот фильтр для контроллера) введена, она также доступна во всех вышеперечисленных родительских элементах (при визуализации в виде дерева) без повторного объявления инъекции, просто не делайте этого. Забудьте об этом, если вы измените ребенка и используете его в родительском. Также я бы рекомендовал никогда не помещать более одного модуля (пространства имен) в файл и никогда не пытаться использовать одно и то же пространство имен в нескольких файлах.
Гэри
Что делать, если у меня есть два файла контроллера или два файла служб? Будет ли создание экземпляра происходить для каждого отдельного файла?
Авинаш Радж
3

Вы получаете ошибку, потому что еще не определили myApp.services. До сих пор я помещал все исходные определения в один файл, а затем использовал их в другом. Как и в вашем примере, я бы добавил:

app.js

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

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

Это должно избавить вас от ошибки, хотя я думаю, вам следует прочитать статью, упомянутую Эдуардом Гамоналем в одном из комментариев.

Торстен Энгельбрехт
источник
Спасибо, Торстен, но я прочитал эту статью и добавил строку angular.module('myApp.services', []);в свой файл app.js. Ни то, ни другое не решает мою проблему.
ChrisDevo