2016-08-19 5 views
40

У меня есть набор угловых компонентов, которые должны получить некоторые инъекции. Моя первая мысль заключалась в том, что было бы лучше создать суперкласс и ввести там службу. Любой из моих компонентов затем расширит этот суперкласс, но этот подход не сработает.Наследование наследования и зависимостей

Упрощенный пример:

export class AbstractComponent { 
    constructor(private myservice: MyService) { 
    // Inject the service I need for all components 
    } 
} 

export MyComponent extends AbstractComponent { 
    constructor(private anotherService: AnotherService) { 
    super(); // This gives an error as super constructor needs an argument 
    } 
} 

я мог бы решить эту проблему путем введения MyService внутри каждого компонента и использовать этот аргумент для super() вызова, но это определенно какой-то абсурд.

Как правильно организовать мои компоненты, чтобы они наследовали сервис из суперкласса?

+0

Это не дубликат. Вопрос, на который делается ссылка, - это о том, как создать класс DERIVED, который может использовать службу, введенную уже определенным суперклассом. Мой вопрос заключается в том, как построить класс SUPER, который наследует службу к производным классам. Это просто наоборот. – maxhb

+0

Ваш ответ (встроенный в ваш вопрос) не имеет для меня смысла. Таким образом, вы создаете инжектор, который не зависит от углового использования форсунки для вашего приложения. Использование 'new MyService()' вместо инъекции дает вам точно такой же результат (кроме более эффективного). Если вы хотите разделить один и тот же экземпляр службы между различными службами и/или компонентами, это не сработает. Каждый класс получит другой экземпляр «MyService». –

+0

Вы совершенно правы, мой код будет генерировать множество экземпляров 'myService'. Нашел решение, которое позволяет избежать этого, но добавляет больше кода к производным классам ... – maxhb

ответ

27

Обновлено решение, предотвращающее создание нескольких экземпляров myService с использованием глобального инжектора.

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

export class AbstractComponent { 
    protected myServiceA:MyServiceA; 
    protected myServiceB:MyServiceB; 
    protected myServiceC:MyServiceC; 

    constructor(injector: Injector) { 
    this.settingsServiceA = injector.get(MyServiceA); 
    this.settingsServiceB = injector.get(MyServiceB); 
    this.settingsServiceB = injector.get(MyServiceC); 
    } 
} 

export MyComponent extends AbstractComponent { 
    constructor(
    private anotherService: AnotherService, 
    injector: Injector 
) { 
    super(injector); 

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

Это будет гарантировать, что MyService может быть использован в любой класс, расширяющий AbstractComponent без необходимости впрыскивать MyService в каждом производном классе.

Есть некоторые минусы этого решения (см Ccomment от @ Гюнтера Zöchbauer ниже моего первоначального вопроса):

  • инъекционного глобального инжектор только улучшение, когда существует несколько различных услуг, которые должны быть введены в многие места. Если у вас только одна совместная услуга, то, вероятно, лучше/проще ввести эту услугу в производный класс.
  • Мое решение и его предлагаемая альтернатива имеют как недостаток, так и затруднение, чтобы определить, какой класс зависит от того, какой сервис ,

Для очень хорошо написано объяснения инъекции зависимостей в Angular2 увидеть этот пост в блоге, который помог мне в значительной степени решить эту проблему: http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html

+4

Это очень затрудняет понимание того, какие услуги фактически вводят. –

+1

У меня есть 4 подкласса и 6 служб для ввода в базовый класс. Повторная инъекция их снова и снова в цепочке у каждого конструктора была действительно болью. Это решение было именно тем, что я искал, – Pac0

+0

Отлично работает, используя это для стандартных услуг, введенных в базовую службу. – NathofGod

25

Я мог бы решить это, введя MyService в каждый компонент и используя этот аргумент для вызова super(), но это определенно какой-то абсурд.

Это не абсурд. Вот как работают конструкторы и инжектор конструктора.

Каждый класс, подлежащий инъекции, должен объявлять зависимости как параметры конструктора, и если у суперкласса также есть зависимости, они также должны быть указаны в конструкторе подкласса и переданы в суперкласс с вызовом super(dep1, dep2).

Проникновение инжектора и получение зависимостей императивно имеет серьезные недостатки.

Он скрывает зависимости, которые затрудняют чтение кода.
Это нарушает ожидания одного из знакомых с тем, как работает Angular2 DI.
Он разбивает автономную компиляцию, которая генерирует статический код для замены декларативного и императивного DI для повышения производительности и уменьшения размера кода.

+0

Если мне нужно передать необходимую службу из каждого производного класса в суперкласс, то бессмысленно пытаться вводите его в суперклассу. Просто добавьте его в каждый производный класс. Меньше кода, лучше читаемость кода. – maxhb

+0

Конечно, просто вводите его везде, где вы на самом деле нуждаетесь в нем. Если у вашего суперкласса есть определенная реализация, которая зависит от него, добавьте его в конструктор, чтобы подклассы должны были передать его, иначе просто нет. –

+1

Просто, чтобы прояснить: мне это нужно ВЕЗДЕ. Попытка перенести эту зависимость на мой суперклассу так, чтобы производный класс EACH мог получить доступ к службе без необходимости вводить ее по отдельности каждому производному классу. – maxhb

1

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

производный класс:

@Component({ 
    ... 
    providers: [ProviderService] 
}) 
export class DerivedComponent extends BaseComponent { 
    constructor(protected providerService: ProviderService) { 
     super(providerService); 
    } 
} 

Базового класс:

export class BaseComponent { 
    constructor(protected providerService: ProviderService) { 
     // do something with providerService 
    } 
} 

сервис-обеспечение класс:

@Injectable() 
export class ProviderService { 
    constructor(private _apiService: ApiService, private _authService: AuthService) { 
    } 
} 
+0

Проблема заключается в том, что вы рискуете создать «нежелательный ящик», который по сути является прокси-сервером для службы Injector. – kpup

0

Если родительский класс был приобретен от третьей партии плагина (и вы не можете изменить источник), вы можете сделать это:

import { Injector } from '@angular/core'; 

export MyComponent extends AbstractComponent { 
    constructor(
    protected injector: Injector, 
    private anotherService: AnotherService 
) { 
    super(injector.get(MyService)); 
    } 
} 

или самый лучший способ (остаться только один параметр в конструкторе):

import { Injector } from '@angular/core'; 

export MyComponent extends AbstractComponent { 
    private anotherService: AnotherService; 

    constructor(
    protected injector: Injector 
) { 
    super(injector.get(MyService)); 
    this.anotherService = injector.get(AnotherService); 
    } 
} 
0

Вместо введения услуги, которая имеет все другие услуги, как зависимости, например так:

class ProviderService { 
    constructor(private service1: Service1, private service2: Service2) {} 
} 

class BaseComponent { 
    constructor(protected providerService: ProviderService) {} 

    ngOnInit() { 
     // Access to all application services with providerService 
     this.providerService.service1 
    } 
} 

class DerivedComponent extends BaseComponent { 
    ngOnInit() { 
     // Access to all application services with providerService 
     this.providerService.service1 
    } 
} 

I пропустит этот дополнительный шаг и просто добавит все услуги в BaseComponent, например:

class BaseComponent { 
    constructor(protected service1: Service1, protected service2: Service2) {} 
} 

class DerivedComponent extends BaseComponent { 
    ngOnInit() { 
     this.service1; 
     this.service2; 
    } 
} 

Этот метод предполагает 2 вещи:

  1. Ваша забота полностью связана с наследованием компонентов. Скорее всего, причина, по которой вы попали на этот вопрос, связана с подавляющим количеством несухих (WET?) Кодов, которые нужно повторять в каждом производном классе. Если вы хотите получить преимущества от одной точки входа для всех ваших компонентов и услуг, вам нужно будет сделать дополнительный шаг.

  2. Каждый компонент расширяет BaseComponent

Существует также недостаток, если вы решили использовать конструктор производного класса, как вам нужно будет позвонить super() и передать всех зависимостей. Хотя я действительно не вижу варианта использования, который требует использования constructor вместо ngOnInit, вполне возможно, что такой вариант использования существует.