ViewProviders in Angular

Sinan Öztürk
3 min readMar 14, 2024

--

First of all we can only use viewProviders key in @Component metadata.

So when to use ViewProviders? There are 2 use cases for viewProviders.

1-) Provide Only for View

What do i mean by that? What is View?

  • View is as you can understand from the name, it’s a component view itself, it’s a component template.
  • But we can already provide dependencies for our template with providers array. Why should i use viewProviders?
  • viewProviders doesn’t provide for projected content components. If you dont know what is content projection and how to use ng-content check this blog post of mine.
  • If you are okey with ng-content let’s see an usage example.

app.component.ts

@Component({
selector: 'app-root',
standalone: true,
imports: [ParentComponent, CustomContentComponent],
template: `
<app-parent>
<app-custom-content> </app-custom-content>
</app-parent>
`,
})
export class AppComponent {}

We have ParentComponent and this component gets an content.

parent.component.ts

@Component({
selector: 'app-parent',
standalone: true,
imports: [RandomComponent],
template: `
<div>
<app-random></app-random>
<h4>Ng Content</h4>
<ng-content></ng-content>
</div>
`,
viewProviders: [{ provide: EmojiService, useValue: '👪' }],
})
export class ParentComponent {}

random.component.ts

@Component({
selector: 'app-random',
standalone: true,
template: `randomComponent: {{ emoji }}`,
})
export class RandomComponent {
emoji = inject(EmojiService, { optional: true });
}

custom-content.component.ts

@Component({
selector: 'app-custom-content',
standalone: true,
template: `CustomContentComponent: {{ emoji }}`,
})
export class CustomContentComponent {
emoji = inject(EmojiService, { optional: true });
}

CustomContentComponent won’t able to get provided emoji because it’s only provided for view not for projected content (ng-content).

Result

2-) Provide dependency when a child directive uses @Host resolution modifier

  • Don’t forget, every component is an also directive.

I want to explain this case with an example.

app.component.ts

@Component({
selector: 'app-root',
standalone: true,
imports: [CustomDirective],
template: ` <div appCustom>App Component</div> `,
providers: [{ provide: EmojiService, useValue: '🍒' }],
})
export class AppComponent {}

custom.directive.ts

@Directive({
selector: '[appCustom]',
standalone: true,
})
export class CustomDirective {
constructor(@Optional() @Host() public emoji: EmojiService) {
console.log(emoji);
}
}

So if i check the console it will be null.

It’s because @Host resolution modifier look for dependency in the view, it doesn’t check the component injector.

So, if i change my app.component.ts as below it will work.

@Component({
selector: 'app-root',
standalone: true,
imports: [CustomDirective],
template: ` <div appCustom>App Component</div> `,
viewProviders: [{ provide: EmojiService, useValue: '🍒' }],
})
export class AppComponent {}

It will log the 🍒

Read my article about @Self and @Host resolution modifiers.

--

--