Улучшите свою архитектуру Angular

Раньше я передавал услуги конструктору компонента следующим образом:

import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class MyComponent {
  constructor(
    private myServiceA:MyServiceA, 
    private myServiceB:MyServiceB, 
    private myServiceC:MyServiceC) {}
}

Это хорошо, но когда ваше приложение разрастется, вы можете рассмотреть другой подход, который сделает вашу архитектуру более масштабируемой. Создадим базовый компонент, или так называемый контейнер, который будет содержать глобальный инжектор. Этот файл может называться featureA.base.container.ts

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

В отличие от первого фрагмента, где мы передаем сервисы в качестве параметров конструктору, мы можем импортировать Injector из ядра Angular и передать только это. Затем, используя метод get, мы можем получить экземпляры других сервисов. Вот как это выглядит (исправленный фрагмент из: Наследование и внедрение зависимостей)

// featureA.base.container.ts
import {Injector} from '@angular/core';
import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class BaseComponent {
  protected myServiceA:MyServiceA;
  protected myServiceB:MyServiceB;
  protected myServiceC:MyServiceC;
  sharedProperty: boolean;
constructor(injector: Injector) {
    this.myServiceA = injector.get(MyServiceA);
    this.myServiceB = injector.get(MyServiceB);
    this.myServiceC = injector.get(MyServiceC);
  }
}

А вот как использовать его в дочернем компоненте. Нам нужно extend компоненты, которые будут использовать эти сервисы. Помните, как показано на диаграмме выше, речь идет также об общем доступе к свойствам и методам.

export MyComponent extends BaseComponent {
  constructor(injector: Injector) {
    super(injector);

    this.myServiceA.JustCallSomeMethod();
    this.myServiceB.JustCallAnotherMethod();
    this.myServiceC.JustOneMoreMethod();
    this.sharedProperty = true;
  }
}

Мы снова используем Injector и ключевое слово super(injector), которое вызывает родительский конструктор.

Ключевое слово super используется для доступа к свойствам литерала объекта или класса [[Prototype]] или для вызова конструктора суперкласса.

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

Похожий паттерн описан в разделе Проектирование архитектуры Angular — паттерн Container-Presentation, где декораторы ввода и вывода используются для передачи данных между компонентами. Независимо от вашего выбора шаблона, я надеюсь, что вы нашли эту статью полезной.