Angular DI providers vs viewProviders

Viewed 883

I am trying to understand Angular's DI framework and referring to the official docs in https://angular.io/guide/hierarchical-dependency-injection; particularly the difference between viewProviders and providers attributes.

While I can't say that the content is easy to follow (especially the section where it explains how it works under the hood using the combined logical tree and using the notations @Provide and @Inject sometimes in <#VIEW> and sometimes in the selector tag of the component), I concluded that using viewProviders instead of providers can affect the rendering of a projected content using <ng-content>

Coming to the question, I don't see an explanation to a scenario if the same DI token is used both in providers and viewProviders.

Eg.

Referring to the snippet from parent.component.ts, my question is : If by virtue of un-commenting the providers attribute enables rendering the value "world" as configured in the element injector, instead of the value "hello" in module injector, why is it disregarding the value "angular"?

viewProviders: [{ provide: Service, useValue: { value: 'world' } }],
  // providers: [{ provide: Service , useValue: { value: 'angular' } }], // If I uncomment this line the "hello" changes to "world" and the value "angular" is disregarded

in https://stackblitz.com/edit/angular-syh4mx

It's probably not a valid use case but I'm interested to understand why it behaves the way it does.

1 Answers

Basing on this documentation you are getting value injected to the closest injector. If you have commented out providers the closes injector is global injector and if its uncommented closest injector is on component.

It kind of looks like a bug because global injector by intuition also is less important than viewProvider and when provider is not present it should behave similarly. But it its intended behavior as you can see in unit test which defines how it behaves when having both providers and viewProviders defined. And you can find the exact answer to your question at the end of this section

<app-root @NgModule(AppModule)
        @Inject(FlowerService) flower=>"">
  <#VIEW>
    <p>Emoji from FlowerService: {{flower.emoji}} ()</p>
    <app-child @Provide(FlowerService="")
               @Inject(FlowerService)=>""> <!-- search ends here -->
      <#VIEW> <!-- search starts here -->
        <h2>Parent Component</h2>
        <p>Emoji from FlowerService: {{flower.emoji}} ()</p>
      </#VIEW>
     </app-child>   </#VIEW> </app-root>

When requests the FlowerService, the injector begins its search at the <#VIEW> belonging to (<#VIEW> is included because it is injected from @Component()) and ends with .

<#VIEW> is included because it is injected from @Component() so like if providers are part of annotation then its triggers including view as part of lookup list and having a view on lookup list checks value in viewProviders first.

You can find code related to this behavior here

Related