Сміливий стайлгайд по AngularJS для командної розробки [1/2]

       Після прочитання Google's AngularJS Guidelines , у мене склалося враження про його незавершеності, а ще в ньому часто натякали на профіт від використання бібліотеці Closure. Ще вони заявили , «Ми не думаємо, що ці рекомендації однаково добре застосовні для всіх проектів, що використовують AngularJS. Ми будемо раді бачити ініціативу від спільноти за більш загальний стайлгайд, який можна застосовувати як для невеликих так і великих проектів ».
 
Відштовхуючись від особистого досвіду роботи з Angular, кількох виступів , а також наявним досвіду командної розробки, представляю Вашій увазі цей сміливий стайлгайд по синтаксису, написанню коду і структурі додатків на AngularJS.
 
 Визначення модулів
Модулі в AngularJS можуть бути оголошені різними способами: або з використанням змінної, або через getter-синтаксис. Завжди використовуйте другий спосіб (більш того, він рекомендований розробниками фреймворка ).
 
 
Погано:
 
var app = angular.module('app', []);
app.controller();
app.factory();

 
Добре:
 
angular
  .module('app', [])
  .controller()
  .factory();

 Функції і методи модуля
В модулях Angular є багато різних методів, таких як
controller
,
factory
,
directive
,
service
і ін. Є також багато різних синтаксисів для модулів, коли мова заходить про DI і форматуванні коду. Використовуйте визначення іменованих функції, щоб потім передавати їх назви відповідним методам. Такий спосіб дає більше можливості при трасуванні стека, тому що функції не є анонімними (звичайно, можна просто почати використовувати іменовані функції замість анонімних, але такий спосіб більш удобочитаем).
 
 
Погано:
 
var app = angular.module('app', []);
app.controller('MyCtrl', function () {

});

 
Добре:
 
function MainCtrl () {

}

angular
  .module('app', [])
  .controller('MainCtrl', MainCtrl);

Визначайте модуль одного разу використовуючи setter-синтаксис так:
angular.module('app', [])
. Потім, якщо знадобитися звернутися до цього модулю (наприклад, в інших файлах), використовуйте getter-синтаксис:
angular.module('app')
.
 
А для того, щоб не забруднювати глобальну область видимості, просто оберніть весь свій код в IIFE.
 
 
Відмінно:
 
(function () {
  angular.module('app', []);
  
  // MainCtrl.js
  function MainCtrl () {

  }
  
  angular
    .module('app')
    .controller('MainCtrl', MainCtrl);
    
  // AnotherCtrl.js
  function AnotherCtrl () {
  
  }
  
  angular
    .module('app')
    .controller('AnotherCtrl', AnotherCtrl);
    
  // и так далее...
    
})();

 Контролери
З причини того, що контролери — це класи, крім звичного
controller
-сінтаксіса у них є
controllerAs
-сінтаксіс. Використовуйте саме його, тому що крім можливості посилатися на екземпляр контролера, такий спосіб робить Скоуп вкладеними.
 
 Прив'язка до DOM через controllerAs
 
Погано:
 
<div ng-controller="MainCtrl">
  {{ someObject }}
</div>

 
Добре:
 
<div ng-controller="MainCtrl as main">
  {{ main.someObject }}
</div>

Варто також зауважити, що спосіб прив'язки до DOM через аттрибут
ng-controller
в чому обмежує застосування даного подання (view) тільки в парі з зазначеним контролером. І хоча рідко, але все ж бувають ситуації, коли один і той же подання може бути використано з різними контролерами. Для більшої гнучкості в даному питанні, використовуйте роутер для зв'язку view з контролером.
 
 
Відмінно:
 
<!-- main.html -->
<div>
  {{ main.someObject }}
</div>
<!-- main.html -->

<script>
// ...
function config ($routeProvider) {
  $routeProvider
  .when('/', {
    templateUrl: 'views/main.html',
    controller: 'MainCtrl',
    controllerAs: 'main'
  });
}
angular
  .module('app')
  .config(config);
//...
</script>

Щоб уникнути використання
$parent
, коли потрібно отримати доступ до якого-небудь з батьківських контролерів, при такому підході просто пишемо значення
controllerAs
необхідного контролера, в нашому випадку
main
. Зрозуміло, що завдяки такому способу, ми уникаємо також викликів кшталт
$parent.$parent
.
 
 this в controllerAs
Синтаксис
controllerAs
увазі використання ключового слова
this
в коді контролера замість
$scope
. При використанні
controllerAs
, контролер за фактом прив'язаний до
$scope
, що, власне, і дає таку ступінь поділу.
 
 
Погано:
 
function MainCtrl ($scope) {
  $scope.someObject = {};
  $scope.doSomething = function () {
  
  };
}
angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Можна також використовувати
prototype
для створення класів контролера, але це швидко забруднить код, тому що кожному DI-провайдеру необхідна відповідна посилання в конструкторі.
 
 
Погано і добре:
Добре для наслідування, але погано для загального використання.
 
 
function MainCtrl ($scope) {
  this.someObject = {};
  this._$scope = $scope;
}
MainCtrl.prototype.doSomething = function () {
  // use this._$scope
};
angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Якщо ви використовуєте
prototype
але не знаєте навіщо, то це погано. Якщо ви використовуєте
prototype
для ізоляції від інших контролерів — це добре. А для загального випадку використання
prototype
може бути попросту надлишковим.
 
 
Добре:
 
function MainCtrl () {
  this.someObject = {};
  this.doSomething = function () {
  
  };
}
angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Вище просто показаний приклад використання об'єктів і функцій в контролері. Звичайно ж, це зовсім не означає, що ми збираємося використовувати там логіку…
 
 Уникайте використання логіки в контроллерах
Делегируйте логіку фабрикам і сервісам.
 
 
Погано:
 
function MainCtrl () {
  this.doSomething = function () {

  };
}
angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

 
Добре:
 
function MainCtrl (SomeService) {
  this.doSomething = SomeService.doSomething;
}
angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Цей підхід максимізує повторне використання коду, надійно инкапсулирует його функціональність і робить тестування більш легким і точним.
 
 Сервіси
Сервіси інстанцірует, відповідно вони повинні бути классо-подібними. Саме з цього тут ми також використовуємо
this
, оформляємо код функцій відповідно з усім іншим.
 
 
Добре:
 
function SomeService () {
  this.someMethod = function () {

  };
}
angular
  .module('app')
  .service('SomeService', SomeService);

 Фабрики
Фабрики дають нам Сінглтон-модуль, для створення сервісних методів (таких, наприклад, як зв'язок додатки з сервером за допомогою REST). Створення та повернення за запитом цільного об'єкта підтримує існуючі прив'язки (binds) в контролері оновленими, а ще допомагають уникнути проблем з прив'язкою примітивів.
 
Важливо: Насправді «Фабрика» — це шаблон / реалізація і не повинно ототожнюватися з провайдером. Правильніше буде називати і фабрики, і сервіси «сервісами».
 
 
Погано:
 
function AnotherService () {

  var someValue = '';

  var someMethod = function () {

  };
  
  return {
    someValue: someValue,
    someMethod: someMethod
  };

}
angular
  .module('app')
  .factory('AnotherService', AnotherService);

 
Добре:
Спочатку ми створюємо однойменний об'єкт всередині функції, а потім наповнюємо його методами. Це сприяє як ручному документуванню коду, так і генерації документації автоматичними засобами.
 
 
function AnotherService () {

  var AnotherService = {};
  
  AnotherService.someValue = '';

  AnotherService.someMethod = function () {

  };
  
  return AnotherService;
}
angular
  .module('app')
  .factory('AnotherService', AnotherService);

Тут все прив'язки до примітивів залишаються оновленими, а ще це робить внутремодульную організацію простору назв трохи простіший для розуміння — тут відразу видно приватні методи і змінні.
 
 Далі буде…
В наступній частині перекладу:
 
     
Директиви
 Promise в роутере, defer в контролері
 Уникайте $ scope. $ watch
 Структура проекту
 Угода про іменування і конфлікти
 Мініфікація і анотація
 
Даний стайлгайд знаходиться в процесі доопрацювання. Завжди актуальні рекомендації Ви знайдете на Github .
  
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.