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.
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).
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
Look at the example app at stackblitz
The app has RandomService, which generates a random number when initialized.
random-service.ts
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
child.component.ts
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.
grand-child.component.ts
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.
app.component.ts
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.
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.
<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.