Завдяки величезній кількості цінних джерел, я отримав кілька загальних рекомендацій щодо впровадження компонентів у додатках AngularJS:
Контролер
Контролер повинен бути лише прошарком між моделлю та видом. Постарайтеся зробити це якомога тонкішим .
Настійно рекомендується уникати ділової логіки в контролері. Його слід перенести на модель.
Контролер може спілкуватися з іншими контролерами, використовуючи виклик методу (можливо, коли діти хочуть спілкуватися з батьком) або $ emit , $ Broadcast та $ на методах. Повідомлення, що передаються та транслюються, повинні бути зведені до мінімуму.
Контролер не повинен дбати про презентацію чи маніпуляції з DOM.
Намагайтеся уникати вкладених контролерів . У цьому випадку батьківський контролер інтерпретується як модель. Натомість введіть моделі як спільні служби.
Область контролера повинна використовуватися для моделі прив'язки з видом і
інкапсуляцією представленої моделі, як для моделі дизайну презентаційної моделі .
Область застосування
Розглядайте сферу дії як шаблони лише для читання в шаблонах і лише для запису в контролерах . Метою сфери застосування є посилання на модель, а не на модель.
Виконуючи двонаправлене прив'язування (ng-модель), переконайтеся, що ви не прив'язуєте безпосередньо до властивостей області.
Модель
Модель в AngularJS - сингл, визначений сервісом .
Модель забезпечує відмінний спосіб розділення даних та відображення.
Моделі є головними кандидатами для тестування одиниць, оскільки вони, як правило, мають однакову залежність (певна форма випромінювача подій, у звичайному випадку $ rootScope ) і містять чітко перевірену логіку домену .
Модель слід розглядати як реалізацію певного підрозділу. Він заснований на принципі єдиної відповідальності. Блок - це екземпляр, який відповідає за власну сферу пов'язаної логіки, яка може представляти єдину сутність у реальному світі та описувати її у світі програмування з точки зору даних та стану .
Модель має інкапсулювати дані вашої програми та надати API
для доступу та маніпулювання цими даними.
Модель повинна бути портативною, щоб її можна було легко транспортувати до подібної програми.
Виділяючи логіку одиниці у вашій моделі, ви спростили пошук, оновлення та обслуговування.
Модель може використовувати методи більш загальних глобальних моделей, які є загальними для всієї програми.
Постарайтеся уникати складу інших моделей у вашій моделі, використовуючи нагнітання залежностей, якщо це насправді не залежить від зменшення з’єднання компонентів та підвищення рівня перевіреності та зручності використання одиниць .
Намагайтеся уникати використання слухачів подій у моделях. Це ускладнює їх тестування і, як правило, вбиває моделі з точки зору принципу єдиної відповідальності.
Впровадження моделі
Оскільки модель повинна містити певну логіку з точки зору даних та стану, вона повинна архітектурно обмежувати доступ до своїх членів, таким чином ми можемо гарантувати слабку зв'язок.
Спосіб зробити це в застосуванні AngularJS - це визначити його за допомогою фабричного типу обслуговування. Це дозволить нам дуже легко визначити приватні властивості та методи, а також повернути публічно доступні в одне місце, що зробить його справді читабельним для розробника.
Приклад :
angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {
var itemsPerPage = 10,
currentPage = 1,
totalPages = 0,
allLoaded = false,
searchQuery;
function init(params) {
itemsPerPage = params.itemsPerPage || itemsPerPage;
searchQuery = params.substring || searchQuery;
}
function findItems(page, queryParams) {
searchQuery = queryParams.substring || searchQuery;
return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages <= currentPage;
return results.list
});
}
function findNext() {
return findItems(currentPage + 1);
}
function isAllLoaded() {
return allLoaded;
}
// return public model API
return {
/**
* @param {Object} params
*/
init: init,
/**
* @param {Number} page
* @param {Object} queryParams
* @return {Object} promise
*/
find: findItems,
/**
* @return {Boolean}
*/
allLoaded: isAllLoaded,
/**
* @return {Object} promise
*/
findNext: findNext
};
});
Створення нових екземплярів
Постарайтеся уникати фабрики, яка повертає нову функціональну функцію, оскільки це починає руйнувати залежність залежностей, і бібліотека буде вести себе ніяково, особливо для третіх сторін.
Кращий спосіб здійснити те ж саме - використовувати фабрику як API для повернення колекції об'єктів із приєднаними до них методами getter та setter.
angular.module('car')
.factory( 'carModel', ['carResource', function (carResource) {
function Car(data) {
angular.extend(this, data);
}
Car.prototype = {
save: function () {
// TODO: strip irrelevant fields
var carData = //...
return carResource.save(carData);
}
};
function getCarById ( id ) {
return carResource.getById(id).then(function (data) {
return new Car(data);
});
}
// the public API
return {
// ...
findById: getCarById
// ...
};
});
Глобальна модель
Як правило, намагайтеся уникати подібних ситуацій і правильно конструюйте свої моделі, щоб вони могли бути введені в контролер і використані на ваш погляд.
Зокрема, деякі методи вимагають глобальної доступності всередині програми. Щоб зробити це можливим, ви можете визначити властивість ' common ' у $ rootScope та прив’язати його до commonModel під час завантаження програми:
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
Усі ваші глобальні методи житимуть у межах " загальної " властивості Це якийсь простір імен .
Але не визначайте жодних методів безпосередньо у вашому $ rootScope . Це може призвести до несподіваної поведінки при використанні директиви ngModel у межах вашої області перегляду, як правило, засмічує сферу вашої діяльності та призводить до вирішення проблем, що вирішують методи застосування.
Ресурс
Ресурс дозволяє взаємодіяти з різними джерелами даних .
Потрібно реалізовуватись за принципом єдиної відповідальності .
Зокрема, це проксі для багаторазового використання для кінцевих точок HTTP / JSON.
Ресурси вводяться в моделі і забезпечують можливість надсилання / отримання даних.
Реалізація ресурсів
Фабрика, яка створює ресурсний об'єкт, що дозволяє взаємодіяти з джерелами даних на сервері RESTful.
Об'єкт, що повертається, має методи дії, які забезпечують поведінку на високому рівні без необхідності взаємодії з послугою $ http низького рівня.
Послуги
І модель, і ресурс - це послуги .
Служби - це не асоційовані, нещільно поєднані одиниці функціональності, які є самостійними.
Послуги - це особливість, яку Angular пропонує веб-додаткам на стороні клієнта з боку сервера, де сервіси широко використовуються вже давно.
Послуги в кутових додатках - це замінювані об'єкти, які з'єднані між собою за допомогою введення залежності.
Кутовий постачається з різними видами послуг. У кожного є власні випадки використання. Будь ласка, прочитайте Розуміння типів послуг для отримання детальної інформації.
Спробуйте врахувати основні принципи архітектури обслуговування у вашій програмі.
Загалом відповідно до Глосарію веб-служб :
Послуга - це абстрактний ресурс, який представляє можливість виконання завдань, що формують цілісну функціональність з точки зору організацій-постачальників та суб'єктів-запитувачів. Для використання послуга повинна бути реалізована конкретним агентом провайдера.
Клієнтська структура
Загалом клієнтська частина програми розділена на модулі . Кожен модуль повинен бути перевірений як одиниця.
Спробуйте визначити модулі залежно від функції / функціональності чи перегляду , а не за типом. Детальніше дивіться у презентації Місько .
Компоненти модулів можуть бути умовно згруповані за типами, такими як контролери, моделі, представлення даних, фільтри, директиви тощо.
Але сам модуль залишається для багаторазового використання , передається та перевіряється .
Також розробникам набагато простіше знайти деякі частини коду та всі його залежності.
Для детальної інформації зверніться до Організації коду у великих програмах AngularJS та JavaScript .
Приклад структуризації папок :
|-- src/
| |-- app/
| | |-- app.js
| | |-- home/
| | | |-- home.js
| | | |-- homeCtrl.js
| | | |-- home.spec.js
| | | |-- home.tpl.html
| | | |-- home.less
| | |-- user/
| | | |-- user.js
| | | |-- userCtrl.js
| | | |-- userModel.js
| | | |-- userResource.js
| | | |-- user.spec.js
| | | |-- user.tpl.html
| | | |-- user.less
| | | |-- create/
| | | | |-- create.js
| | | | |-- createCtrl.js
| | | | |-- create.tpl.html
| |-- common/
| | |-- authentication/
| | | |-- authentication.js
| | | |-- authenticationModel.js
| | | |-- authenticationService.js
| |-- assets/
| | |-- images/
| | | |-- logo.png
| | | |-- user/
| | | | |-- user-icon.png
| | | | |-- user-default-avatar.png
| |-- index.html
Хороший приклад структурування кутових додатків реалізований програмою angular-app - https://github.com/angular-app/angular-app/tree/master/client/src
Це враховують і сучасні генератори додатків - https://github.com/yeoman/generator-angular/isissue/109