Лучшие практики AngularJS для объявления модуля?

115

В моем приложении объявлено несколько модулей Angular. Изначально я начал объявлять их, используя синтаксис «цепочки», например:

angular.module('mymodule', [])
    .controller('myctrl', ['dep1', function(dep1){ ... }])
    .service('myservice', ['dep2', function(dep2){ ... }])
    ... // more here

Но я решил, что это нелегко прочитать, поэтому начал объявлять их, используя переменную модуля, подобную этой:

var mod = angular.module('mymodule', []);

mod.controller('myctrl', ['dep1', function(dep1){ ... }]);

mod.service('myservice', ['dep2', function(dep2){ ... }]);
...

Второй синтаксис кажется мне более читаемым, но моя единственная жалоба заключается в том, что этот синтаксис оставляет modпеременную вне глобальной области видимости. Если у меня когда-либо будет указана другая переменная mod, она будет заменена следующей (и другими проблемами, связанными с глобальными переменными).

Итак, мой вопрос: это лучший способ? Или лучше сделать что-нибудь подобное ?:

(function(){
    var mod = angular.module('mymod', []);
    mod.controller('myctrl', ['dep1', function(dep1){ ... }]);
    mod.service('myservice', ['dep2', function(dep2){ ... }]);
    ...
})();

Или это достаточно важно, чтобы заботиться? Просто любопытно узнать, каковы «лучшие практики» для объявления модуля. Заранее спасибо.

tennisgent
источник
3
Я тоже задавался вопросом об Angular Best Practices
Dalorzo

Ответы:

118

'Лучший' способ объявить модуль

Поскольку angular находится в самой глобальной области видимости, а модули сохраняются в его переменной, вы можете получить доступ к модулям через angular.module('mymod'):

// one file
// NOTE: the immediately invoked function expression 
// is used to exemplify different files and is not required
(function(){
   // declaring the module in one file / anonymous function
   // (only pass a second parameter THIS ONE TIME as a redecleration creates bugs
   // which are very hard to dedect)
   angular.module('mymod', []);
})();


// another file and/or another anonymous function
(function(){   
 // using the function form of use-strict...
 "use strict";
  // accessing the module in another. 
  // this can be done by calling angular.module without the []-brackets
  angular.module('mymod')
    .controller('myctrl', ['dep1', function(dep1){
      //..
    }])

  // appending another service/controller/filter etc to the same module-call inside the same file
    .service('myservice', ['dep2', function(dep2){ 
    //... 
    }]);

  // you can of course use angular.module('mymod') here as well
  angular.module('mymod').controller('anothermyctrl', ['dep1', function(dep1){
      //..
  }])
})();

Никаких других глобальных переменных не требуется.

Конечно, все зависит от предпочтений, но я думаю, что это лучшая практика, поскольку

  1. вам не нужно загрязнять глобальную область видимости
  2. вы можете получить доступ к своим модулям повсюду и по желанию сортировать их и их функции в разные файлы
  3. вы можете использовать функциональную форму «use strict»;
  4. порядок загрузки файлов не имеет большого значения

Варианты сортировки ваших модулей и файлов

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

/******** sorting by route **********/    
angular.module('home')...
angular.module('another-route')...
angular.module('shared')...

Как вы это сортируете в конечном итоге - дело личного вкуса, а также масштаба и типа проекта. Мне лично нравится группировать все файлы модуля внутри одной и той же папки (упорядоченные по подпапкам директив, контроллеров, служб и фильтров), включая все различные тестовые файлы, поскольку это делает ваши модули более пригодными для повторного использования. Таким образом, в проектах среднего размера я получаю базовый модуль, который включает в себя все основные маршруты и их контроллеры, службы, директивы и более или менее сложные подмодули, когда я думаю, что они могут быть полезны и для других проектов, например :

/******** modularizing feature-sets **********/
/controllers
/directives
/filters
/services
/my-map-sub-module
/my-map-sub-module/controllers
/my-map-sub-module/services
app.js
...

angular.module('app', [
  'app.directives',
  'app.filters',
  'app.controllers',
  'app.services',
  'myMapSubModule'
]);

angular.module('myMapSubModule',[
   'myMapSubModule.controllers',
   'myMapSubModule.services',
   // only if they are specific to the module
   'myMapSubModule.directives',
   'myMapSubModule.filters'
]);

Для очень больших проектов я иногда группирую модули по маршрутам, как описано выше, или по некоторым выбранным основным маршрутам, или даже по комбинации маршрутов и некоторых выбранных компонентов, но это действительно зависит.

РЕДАКТИРОВАТЬ: Просто потому, что это связано, и я снова столкнулся с этим совсем недавно: позаботьтесь о том, чтобы вы создавали модуль только один раз (добавив второй параметр в функцию angular.module). Это испортит ваше приложение, и его будет очень сложно обнаружить.

2015 РЕДАКТИРОВАТЬ модули сортировки: полтора года опыта работы с angular спустя я могу добавить, что преимущества от использования модулей с разными именами в вашем приложении несколько ограничены, поскольку AMD по-прежнему плохо работает с Angular и службами, директивами и фильтрами. в любом случае доступны глобально в контексте angular ( как показано здесь ). Тем не менее, есть семантическое и структурное преимущество, и может быть полезна возможность включать / исключать модуль с одной строкой кода, закомментированной или удаленной.

Также почти никогда не имеет смысла разделять подмодули по типу (например, «myMapSubModule.controllers»), поскольку они обычно зависят друг от друга.

оборота hugo der hungrige
источник
7
Вам не нужен IIFE (Immediately Invoked Function Expression), также известный как анонимная самовыполняющаяся функция
плюс
1
Ты прав. Вам это нужно только тогда, когда вы хотите применить функциональную форму "use strict"; Хотя не больно.
hugo der hungrige
1
В большинстве случаев вы также можете поместить 'use strict';внутрь своего компонента. module.controller(function () { 'use strict'; ... });
Джексон
Мне нравится реализация, но я тоже не люблю связывать цепочки, поэтому я смешиваю это с тем, что делает Бетерраба
Мирко
1
при использовании AMD достаточно одного модуля с именем app. Можно исключить модуль AMD, удалив оператор require. И нам больше не нужно регистрировать контроллеры и сервисы.
Джеймс
28

Мне нравится руководство по angular от Johnpapa, и вот несколько правил, связанных с этим вопросом:

Правило: именованные и анонимные функции

Избегайте использования анонимных функций:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', function() { })

Вместо этого используйте именованные функции:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', Dashboard);

function Dashboard() { }

Как говорит автор: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

Правило: определяйте 1 компонент для каждого файла.

Избегайте использования нескольких компонентов в одном файле:

angular
  .module('app', ['ngRoute'])
  .controller('SomeController', SomeController)
  .factory('someFactory', someFactory);

function SomeController() { }

function someFactory() { }

Вместо этого используйте один файл для определения модуля:

// app.module.js
angular
  .module('app', ['ngRoute']);

один файл просто использует модуль для определения компонента

// someController.js
angular
  .module('app')
  .controller('SomeController', SomeController);

function SomeController() { }

и другой файл для определения другого компонента

// someFactory.js
angular
  .module('app')
  .factory('someFactory', someFactory);

function someFactory() { }

Конечно, есть много других правил для модулей, контроллеров и служб, которые весьма полезны и заслуживают внимания.

И благодаря комментарию ya_dimon приведенный выше код должен быть заключен в IIFE, например:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);
aqingsao
источник
Хороший ответ и отличная ссылка.
Ellesedil
если у меня разные контроллеры в разных файлах javascript, не потребуется ли загрузка большего количества файлов, т.е. большего количества обращений к серверу?
Виньеш Субраманиан
Их довольно легко объединить / ускользнуть / переименовать с помощью gulp или grunt, vignesh, и лично мне нравится gulp.
aqingsao 02
1
вы забыли добавить, что все эти фрагменты должны быть в IIFE, иначе у вас будут такие функции, как «someFactory ()» глобально. Есть вероятность коллизии имен. (и вам не нужен IIFE в es6)
ya_dimon
12

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

// My Controllers File
angular.module('my-controllers',[])
    .controller('oneCtrl',[...])
    .controller('twoCtrl',[...]);

// My Services File
angular.module('my-services',[])
    .factory('oneSrc',[...])
    .facotry('twoSrc',[...]);

// My Directives File
angular.module('my-directives',[])
    .directive('oneDrct',[...])
    .directive('twoDrct',[...]);

// My Main Application File
angular.module('my-app',['my-controllers','my-services','my-directives',...]);

Но каждый из этих файлов становился все больше по мере роста проекта. Поэтому я решил разбить их на отдельные файлы для каждого контроллера или службы. Я обнаружил, что использование angular.module('mod-name').без массива инъекций - это то, что вам нужно, чтобы это работало. Объявление глобальной переменной в одном файле и ожидание того, что она будет легко доступна в другом, просто не работает или может привести к неожиданным результатам.

Вкратце мое приложение выглядело примерно так:

// Main Controller File
angular.module('my-controllers',[]);

// Controller One File
angular.module('my-controllers').controller('oneCtrl',[...]);

//Controller Two File
angular.module('my-controllers').controller('twoCtrl',[...]);

Я сделал это и с файлом служб, нет необходимости изменять основной файл модуля приложения, в который вы все равно будете вставлять те же модули.

meconroy
источник
1
какой смысл создавать отдельные модули для сервисов / директив / контроллеров?
Филип Собчак
2
В крупных проектах бывает сложно найти, когда контроллеры / фильтры / директивы / службы чередуются вместе. Это всего лишь один из способов сохранить порядок.
meconroy
1
@FilipSobczak Он НЕ создает отдельные модули для служб / директив / контроллеров. Скорее, он создал модуль только один раз, используя angular.module('my-controllers',[]);(обратите внимание, что он указывает [] только один раз для объявления). Он просто повторно использует это в других файлах. Разделение файлов позволяет относительно легко поддерживать проект, особенно большой.
Devner 04
8

Еще одна практика - поместить контроллеры, директивы и т. Д. В их собственные модули и вставить эти модули в ваш «основной»:

angular.module('app.controllers', [])
  .controller('controller1', ['$scope', function (scope) {
    scope.name = "USER!";
  }]);

angular.module('app.directives', [])
  .directive('myDirective', [function () {
    return {
      restrict: 'A',
      template: '<div>my directive!</div>'
    }
  }]);

angular.module('app', [
  'app.controllers',
  'app.directives'
]);

В глобальной области ничего не осталось.

http://plnkr.co/edit/EtzzPRyxWT1MkhK7KcLo?p=preview

Мэнни Д.
источник
Почему вы используете app.controllersinsted of в controllersкачестве имени модуля, есть ли какие-то преимущества? Я новичок в Angularjs
sijo vijayan
4

Мне нравится разделять мои файлы и модули.

Что-то вроде этого:

app.js

var myApp = angular.module('myApp', ['myApp.controllers', 'myApp.directives', 'myApp.services']);

myApp.config(['$routeProvider', function($routeProvider) {
    /* routes configs */
    $routeProvider.when(/*...*/);
}]);

directives.js

var myDirectives = angular.module('myApp.directives', []);

myDirectives.directive( /* ... */ );

service.js

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

myServices.factory( /* ... */ );

Я не большой поклонник "цепочечного стиля", поэтому предпочитаю всегда записывать свою переменную.

Beterraba
источник
2
Именно так я и делал, но каждый файл services.js или controller.js быстро становится большим в крупномасштабном проекте, и в конечном итоге вам придется разбить каждую службу или контроллер в отдельный файл.
Meconroy
1
@meconroy Совершенно верно. Когда объект становится все больше и больше, мне нравится разбивать директиву на более мелкие модули и внедрять их в «основной» модуль директивы.
Beterraba
0

Для меня цепочка - самый компактный способ:

angular.module("mod1",["mod1.submod1"])

 .value("myValues", {
   ...
 })

 .factory("myFactory", function(myValues){
   ...
 })

 .controller("MainCtrl", function($scope){

   // when using "Ctrl as" syntax
   var MC = this;
   MC.data = ...;
 })
 ;

Таким образом, я могу легко перемещать компоненты между модулями, мне никогда не нужно объявлять один и тот же модуль дважды, мне не нужны глобальные переменные.

А если файл становится слишком длинным, решение простое - разделить на два файла, каждый из которых объявляет собственный модуль вверху. Для большей прозрачности я стараюсь сохранять по одному уникальному модулю для каждого файла и называть его таким же, как полный путь к файлу. Таким образом, мне никогда не нужно писать модуль без него [], что является распространенной проблемой.

Дмитрий Зайцев
источник