web-dev-qa-db-fra.com

Injection d'héritage et de dépendance

J'ai un ensemble de composants angular2 qui devraient tous recevoir un service injecté. Ma première pensée a été qu'il serait préférable de créer une super classe et d'injecter le service là-bas. N'importe lequel de mes composants pourrait alors étendre cette super-classe, mais cette approche ne fonctionne pas.

Exemple simplifié:

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
  }
}

Je pourrais résoudre ce problème en injectant MyService dans chaque composant et en utilisant cet argument pour l'appel super(), mais c'est une sorte d'absurde.

Comment organiser correctement mes composants afin qu'ils héritent d'un service de la super classe?

71
maxhb

Solution mise à jour, empêche la création de plusieurs instances de myService à l'aide de l'injecteur global.

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();
  }
}

Cela garantira que MyService peut être utilisé dans toute classe qui étend AbstractComponent sans avoir à injecter MyService dans chaque classe dérivée.

Il y a quelques inconvénients à cette solution (voir le commentaire de @ Günter Zöchbauer ci-dessous ma question initiale):

  • L'injection de l'injecteur global ne constitue une amélioration que lorsque plusieurs services différents doivent être injectés dans de nombreux endroits. Si vous ne disposez que d’un service partagé, il est probablement préférable/plus facile d’injecter ce service dans la ou les classes dérivées.
  • Ma solution et son alternative proposée présentent à la fois l’inconvénient de rendre plus difficile la détermination de la classe dépend de quel service.

Pour une explication très bien écrite de l’injection de dépendance dans Angular2, voir cet article de blog qui m’a beaucoup aidé à résoudre le problème: http: // blog. in-angular-2.html

52
maxhb

Je pourrais résoudre ce problème en injectant MyService dans chacun des composants et en utilisant cet argument pour l'appel super (), mais c'est certainement une sorte d'absurde.

Ce n'est pas absurde. Voici comment fonctionnent les constructeurs et l'injection de constructeurs.

Chaque classe injectable doit déclarer les dépendances en tant que paramètres de constructeur. Si la superclasse a aussi des dépendances, celles-ci doivent également être répertoriées dans le constructeur de la sous-classe et transmises à la superclasse avec l'appel super(dep1, dep2).

Passer autour d'un injecteur et acquérir des dépendances a impérativement de sérieux inconvénients.

Il cache les dépendances, ce qui rend le code plus difficile à lire.
Cela va à l’encontre des attentes de ceux qui connaissent bien le fonctionnement de Angular2 DI.
Il rompt la compilation hors ligne qui génère du code statique pour remplacer les ID déclaratives et impératives afin d'améliorer les performances et de réduire la taille du code.

57
Günter Zöchbauer

Au lieu d’injecter manuellement tous les services, j’ai créé une classe fournissant les services, par exemple, elle obtient les services injectés. Cette classe est ensuite injectée dans les classes dérivées et transmise à la classe de base.

Classe dérivée:

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

Classe de base:

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

Classe de prestation de service:

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

Au lieu d'injecter un service qui a tous les autres services comme dépendances, comme ceci:

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
    }
}

Je sauterais cette étape supplémentaire et ajouterais simplement injecter tous les services dans le composant de base, comme ceci:

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

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

Cette technique suppose 2 choses:

  1. Votre préoccupation est entièrement liée à l'héritage des composants. Très probablement, la raison pour laquelle vous avez atterri sur cette question est due à la quantité écrasante de code non sec (WET?) Que vous devez répéter dans chaque classe dérivée. Si vous souhaitez bénéficier d'un seul point d'entrée pour tous vos composants et services, vous devrez effectuer cette étape supplémentaire.

  2. Chaque composant étend le BaseComponent

Il existe également un inconvénient si vous décidez d'utiliser le constructeur d'une classe dérivée, car vous devrez appeler super() et passer toutes les dépendances. Bien que je ne voie pas vraiment un cas d'utilisation nécessitant l'utilisation de constructor à la place de ngOnInit, il est tout à fait possible qu'un tel cas d'utilisation existe.

1
maximedupre

Si la classe parente provient d'un plug-in tiers (et que vous ne pouvez pas changer le source), vous pouvez le faire:

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

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

ou le meilleur moyen (ne reste qu'un paramètre dans le constructeur):

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
dlnsk

D'après ce que j'ai compris pour hériter de la classe de base, vous devez d'abord l'instancier. Afin de l'instancier, vous devez transmettre les paramètres requis de son constructeur afin que vous puissiez les passer d'un enfant à un parent via un appel super (), ce qui est logique. Bien sûr, l'injecteur est une autre solution viable.

0
Ihor Bodnarchuk