ViewProviders are similar to Providers except that the dependencies that you define are visible only to its view children. They are not visible to the Content children.
Table of Contents
ViewProviders
ViewProviders defines the set of injectable services that are visible only to its view DOM children. These services are not visible to views projected via content projection.
We can insert a component (child component) inside another Angular component (Parent Component). The View from that child component is called a View Child (or View Children’s) of the Parent Component.
Angular Components also allow us to insert ( or Project) HTML Content provided by another component. We use the element <ng-content></ng-content>
to create a placeholder for the external content. The view created from such a component is known as Content Child (or Content Children).
Providers Vs ViewProviders
The Services declared in Components Providers are injected to both view children and content children.
But the Services declared in ViewProviders are injected only to view Children.
ViewProviders metadata is available only to Angular Components. Providers are available in NgModule, Component, Pipes, Directives, etc
ViewProviders Example
Look at the example app at stackblitz
The app has RandomService
, which generates a random number when initialized.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import { Injectable } from "@angular/core"; @Injectable({ providedIn: "root" }) export class RandomService { private _randomNo = 0; constructor() { console.log("RandomService Constructed"); this._randomNo = Math.floor(Math.random() * 1000); } get RandomNo() { return this._randomNo; } } |
ChildComponent
displays the random no from the RandomService
.
It also has ng-content, where the parent can inject content. The Parent component is going to inject the GrandChildComponent
here.
ChildComponent
also displays the GrandChildComponent
as View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import { Component, SkipSelf, Self, Optional, Host } from '@angular/core'; import { RandomService } from './random-service'; @Component({ selector: 'my-child', providers: [], viewProviders: [], template: ` <div class="box"> <p>ChildComponent => {{ randomNo }}</p> <ng-content> </ng-content> <strong>View Child</strong> <my-grandChild></my-grandChild> </div> ` }) export class ChildComponent { randomNo; constructor(private randomService: RandomService) { this.randomNo = randomService.RandomNo; } } |
GrandChildComponent
just displays the random no from the RandomService
. We use @Optional()
decorator ensures no error is thrown if the dependency is not found.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { Component, SkipSelf, Self, Optional, Host } from "@angular/core"; import { RandomService } from "./random-service"; @Component({ selector: "my-grandChild", template: ` <div class="box"> GrandChildComponent => {{ randomNo }} </div> `, providers: [], viewProviders: [], }) export class GrandChildComponent { randomNo; constructor(@Optional() private randomService: RandomService) { this.randomNo = randomService?.RandomNo; } } |
In the AppComponent
, we display the ChildComponent
. We also project the GrandChildComponent
inside the ChildComponent
using projected content.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import { Component, VERSION } from '@angular/core'; import { RandomService } from './random-service'; @Component({ selector: 'my-app', providers: [], viewProviders: [], template: ` <div class="box"> <div class="box"> <p>AppComponent => {{ randomNo }}</p> <my-child> <strong>Projected Content</strong> <my-grandChild></my-grandChild> </my-child> </div> ` }) export class AppComponent { randomNo; constructor(private randomService: RandomService) { this.randomNo = randomService.RandomNo; } } |
Run the app and you will see all the components receive the same value for the random no. Because the RandomService is provided from the root module injector.
Also, note that ChildComponent displays the GrandChildComponent
twice. Once as a Content Child (Projected Content) and also a View Child.
Add the RandomService
to the Providers array of the ChildComponent
. As you can see from the image below, ChildComponent
and all its children (content children and view children) get the instance of RandomService
provided by the ChildComponent
.
Now, move the RandomService from Providers to ViewProviders. As you can see in the image below View Child still get the RandomService from ChildComponent, but the Projected Content does not. Projected Content gets the service from the Root Module.
The providers
allow all children to use the services. While the viewProviders
provides services to all its children other than projected content.
The use case for ViewProviders
This is useful when you develop libraries.
For Example, you have made some-great-comp
, which users will use in their user-component
into it.
1 2 3 4 5 | <some-great-comp> <user-component></user-component> </some-great-comp> |
Here you do not want services that you used in your some-great-comp
interfere with the user-component
. Hence you provide your services in the ViewProviders
.
viewProviders: as the name says you want to provide them only to the views not to the standard angular providers in the app!