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

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

Созданные объекты

Существует несколько типов конструкций Angular, которые создаются во время жизненного цикла Angular путем вызова new для аргумента, представляющего конструкцию. К ним относятся controller и service. Когда Angular создает эти объекты для использования $injector, он создает экземпляры объектов. Службы будут созданы один раз в течение жизненного цикла Angular (когда они впервые понадобятся какой-либо конструкции). С этого момента возвращаемая ссылка вводится в каждую конструкцию. Это делает его идеальным способом передачи данных по всему приложению.

Контроллеры создаются для каждого использования (для компонентов, директив, маршрутов и т. д.). Они поддерживают состояние этих отдельных видов использования.

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

Преимущества службы/контроллера Angular как класса

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

Разработчик с меньшей вероятностью объявит вещи разумным образом. Когда все присваивается переменной this (функции и значения), она имеет тенденцию смешивать значения и объявления функций, где нелегко понять, где что-то определяется. Это становится особенно верным, когда функция расширяется.

В классах значения не могут быть определены вне функций.

Кроме того, он также выделяет функции определенного класса из-за нотации.

Недостатки

Есть только один недостаток, на который я жалуюсь. В конструкторе переданные параметры должны быть явно назначены экземпляру и упомянуты в других функциях с помощью this.parameter. Я не возражаю против поведения, если разработчики поддерживают введенное имя (например, MyService назначается экземпляру контроллера путем установки this.MyService = MyService.

Typescript прекрасно справляется с этим, автоматически назначая параметры экземпляру класса, если он определен как общедоступный/частный и т. д., но это совсем другая история.

Пример

function MyController($filter, MyService) {  
    this.format = 'short';
    this.updateDatabase = (viewData) => {
         MyService.updateDatabase(viewData);
    };
    this.$onInit = () => {
         this.dateValue = $filter.get('date')(this.date, this.format);
    });
}

Вышеизложенное зависит от понимания разработчиком стрелочных функций. Если разработчик решил использовать function, то ему пришлось бы ссылаться на экземпляр контроллера с помощью псевдонима ссылки на this.

Приведенное выше также является значительно упрощенным примером… но здесь это как класс:

class MyController {  
   constructor($filter, MyService) {
      const format = 'short';
      Object.assign(this, {
         $filter, MyService, format
      });
   }
   $onInit() {
      this.dateValue = this.$filter.get('date')(this.date, this.format);
   }
   updateDatabase(viewData) {
      this.MyService.updateDatabase(viewData);
   }
}

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

Некоторые рекомендации по использованию классов

  • Конструкторы предназначены для установки констант и внедрения объектов в контроллер, и ничего больше. Это обеспечивает правильную инициализацию значений из привязок и служб и т.п.
  • Используйте $onInit для контроллеров для любой дополнительной настройки необходимых значений, которые рассчитываются (на основе привязок, значений, переданных конструктору и т. д.).
  • Поднимите крючки жизненного цикла Angular наверх. К ним относятся $onInit, $onChanges, $onDestroy и $postLink. Это выделит поведение контроллера, специфичное для Angular. Кроме того, он перемещает вспомогательные функции и просмотр взаимодействий вниз.
  • Используйте Object.assign, чтобы очистить нотацию конструктора, используя сокращенное определение объекта (где имя свойства и переменной совпадают).

Вывод

За три года работы с Angular я полюбил нотацию классов там, где это уместно в приложении. Это сокращает часть многословия объявления функций, а также запутывает поведение ключевого слова this, что полезно для менее опытных разработчиков.