Как не надо писать код на Angular
Angular.js не терпит грубого обращения. Прежде, чем начать использовать фреймворк в своих проектах, нужно настроиться на его волну и понять тонкую архитектуру. В этой заметке я расскажу, что особенно плохо сочетается с идеологией Angular.
- Манипулирование элементами DOM в контроллерах.
Многие из вас работали раньше с проектами на jQuery или Backbone, отсюда проистекает тяга изменять внешний вид объектов внутри кода, обращаясь напрямую к свойствам. Например, так:
var a = $( ‘button.a’ ),
b = $( ‘input[data-role="b"]’ ),
b2 = $( ‘div.status’ ),
flag = true;some.click( function () {
if ( flag ) {
b.hide();
b2.addClass( 'done' );
}
} );
Такой код обычно даст ожидаемый результат, но является стойким анти-паттерном. Смысл Angular заключается как раз в четком разделении представления и бизнес-логики. HTML является представлением, включая структуру элементов и их стили, контроллер же должен отвечать только за поведение. И ничего больше. Он ничего не знает о DOM, CSS-классах и логики обработки событий. В идеале, в контроллере не должно быть обращений к $ или angular.element. Контроллер просто прикрепляет методы к scope, которые затем привязываются к конкретным элементам DOM, c помощью директив (ng-click и тп)
Разделение представления и поведения в Angular существенно облегчит написание unit-тестов ( не потребуется писать тесты для view, проверяющие бизнес-логику).
Даже если вы обходитесь без тестов, это поможет в последствии проще менять функционал.
2. Слишком сложная логика контроллеров
Иногда код в контроллере становится очень запутанным не из-за прямых обращений к DOM, а из-за многочисленных функций в контроллере. Это Тоже очень плохо с точки зрения тестирования: число тестов растет экспоненциально с каждой новой фичей. Это также значит, что легко пропустить много граничных значений. А каждое изменение кода будет нетривиальной задачей.
Если контроллеры слишком перегружены, стоит рассмотреть выделение под-контроллеров. И вообще придерживаться правила одного функционала на контроллер.
Пример такого решения https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views
Другой путь снижения нагрузки на контроллеры- извлечение функционала манипулирования представлением в директивы и соответствующие шаблоны.
Часть логики также можно делегировать сервисам.
3. Отсутствие очистки использованных объектов
Контроллеры часто используют сервисы, такие как $interval (для привязки к времени). Необходимо помнить, что объекты сервисов живут также долго, каки все приложение, а время жизни контроллеров ограничено в общем случае.
Таким образом, когда Angular очищает в памяти объект контроллера, он оставляет все объекты-зависимости. Например, если вы установили таймер в одном из контроллеров, он продолжит “работать” после завершения работы контроллера. Это классика жанра не только для трудно устранимых багов, но и утечек памяти.
4. Засорение $rootScope
Побочный эффект от разделения контроллеров на подконтроллеры — это необходимость решать проблему взаимодействия между ними. И самый простой “способ” решить эту проблему — вынос данных и методов в $rootScope.
Чем это плохо? Когда число объектов и контроллеров, манипулирующих данными в rootScope возрастает, отладка становится невыносимой.
Хороший способ обмена данными между контроллерами — создание соответствующих сервисов для групп контроллеров. В идеале, один из контроллеров изменяет данные, а другие используют эти данные.