Given the following example on stackblitz
@Injectable()
export class LogService {
sayHello() {
console.log('Hello World !');
}
}
@Component({
selector: 'app-child',
standalone: true,
template: `
<div>Child Component !</div>
`,
})
export class ChildComponent {
constructor(@Host() @SkipSelf() logService: LogService) {
logService.sayHello(); // it prints 'Hello World !'
}
}
@Component({
selector: 'app-parent',
imports: [ChildComponent],
standalone: true,
viewProviders: [LogService],
template: `
<h1>Parent Component</h1>
<app-child></app-child>
`,
})
export class ParentComponent {}
I don't understand why logService is correctly injected into ChildComponent despite having @Host() and @SkipSelf() decorators.
The @Host property decorator stops the upward search at the host component. The host component is typically the component requesting the dependency. However, when this component is projected into a parent component, that parent component becomes the host.
Defines the set of injectable objects that are visible to its view DOM children
There is no projected content in this example, so the host component is ChildComponent.
The search should be stopped at ChildComponent and also be skipped for this component , so how can the DI framework see the LogService declared in ParentComponent viewProviders ?
I presumed viewProviders to do the same as providing the service into ChildComponent providers, but @SkipSelf() should prevent from searching inside this component.
Replacing viewProviders by providers produces the expected error No provider for LogService found.
The documentation about viewProviders is very light, I don't understand how it really work behind the scene.
I finally understood how DI resolution works, so I will answer my own question so it could be useful for others.
@SkipSelfskips the current componentproviders/viewProviders.@Hoststops the search when reaching the viewProviders of the host component (host component providers won't be reachable).The host component is the one declaring the current component in its template.
From the example :
@SkipSelfskipsChildComponentproviders and viewProviders.@Hoststops the search atParentComponentviewProviders. The host component isParentComponentbecause it declares<app-child></app-child>in its template.So
LogServiceis eventually resolved fromParentComponentviewProviders.Extra Notes about viewProviders and providers differences
viewProvidersare resolved beforeprovidersviewProvidersandprovidersdependency resolution differs when using content projection.viewProvidersare only visible to the components declared in the template view.I created a stackblitz project in case you want to play with it.
To resolve
viewProviders, you should ask yourself "In which template is declaredapp-childrentag ?". The answer isRootComponentso it has access to itsviewResolvers(along with those from its ancestor if any).It doesn't have access to
ParentComponentviewResolversbecause content projection (throughng-content) is used,viewResolversare not accessible to projected content.