Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way how to use Angular multi providers from all multiple levels?

I am wondering whether it is possible to get the angular multi-providers from (ideally) all ancestors.

Lets say that I have a INJECTION_TOKEN X and I have a component structure like this:

<comp-a>
    <comp-b>
       <comp-c></comp-c>
    <comp-b>
<comp-a>

comp-a providers: providers: {provide: X, useValue: "A", multi: true}

comp-b providers: providers: {provide: X, useValue: "B", multi: true}

Is there a way how to get ["A", "B"] in comp-c when I use Dependency injection like:

constructor(@Inject(X) obtainedArray:TypeOfX[]) {
    console.log(obtainedArray.length); //Expected to be 2
}

I have tried to use this provider in comp-b but it is throwing a cyclic DI expection:

providers:[
    {provide: X, useExisting: X, multi: true}
    {provide: X, useValue: "B", multi: true}
]
like image 899
Palpatine1991 Avatar asked Mar 21 '18 12:03

Palpatine1991


1 Answers

As the following article states:

  • What you always wanted to know about Angular Dependency Injection tree

Angular stores providers on element by using prototypical inheritance. So, never mind whether you use multi or not you will get the following object that contains all providers on current element:

enter image description here

As you can see all providers are here, but since angular just uses square brackets to get provider from element you will get only nearest provider.

To workaround this you can use addition token that uses factory to collect all parent providers:

import { Component, VERSION, InjectionToken, 
  Inject, SkipSelf, Optional } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <comp-a>
        <comp-b>
            <comp-c></comp-c>
        </comp-b>
    </comp-a>
  `
})
export class AppComponent { }

const X = new InjectionToken('X');

const XArray = new InjectionToken('XArray');

const XArrayProvider = {
  provide: XArray,
  useFactory: XFactory,
  deps: [X, [new SkipSelf(), new Optional(), XArray]]
};

export function XFactory(x: any, arr: any[]) {
  return arr ? [x, ...arr] : [x];
}

@Component({
  selector: 'comp-a',
  template: `<ng-content></ng-content>`,
  providers: [
    { provide: X, useValue: "A" },
    XArrayProvider
  ]
})
export class CompA { }


@Component({
  selector: 'comp-b',
  template: `<ng-content></ng-content>`,
  providers: [
    { provide: X, useValue: "B" },
    XArrayProvider
  ]
})
export class CompB { }


@Component({
  selector: 'comp-c',
  template: `{{ tokens }}`
})
export class CompC {
  constructor( @Inject(XArray) public tokens: any[]) { }
}

Ng-run Example

like image 97
yurzui Avatar answered Oct 07 '22 22:10

yurzui