Reputation:
If a module has services defined in the providers array and we import it in the root module, does it mean that those services will become available to all components in the application with the same instance?
For example :
The coreModule imports the HttpClientModule and then we import the coreModule in the AppModule, hence all modules imported in the AppModule will have access to the HttpClient service, how is it possible?
Upvotes: 0
Views: 1828
Reputation: 1771
tl;dr - Yes, all components and descendant components in the module's descendant tree (which starts with the child components listed in the module's declarations
array and works down) will have access to any instance of a class/dependency (like a service) that is either:
AppModule
's providers
array orCoreModule
) and is imported by the module with the declarations
array (e.g. AppModule
)Take the following example:
test.service.ts
@Injectable() // without { providedIn: 'root' }
export class TestService {
constructor() { console.log('TestService loaded')
}
test-service-providing.module.ts
@NgModule({
providers: [TestService],
})
export class TestServiceProvidingModule {}
app.module.ts
@NgModule({
imports: [TestServiceProvidingModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
app.component.ts
@Component({
selector: 'my-app',
template: `<p>App</p>`,
})
export class AppComponent {
constructor() {}
}
TestService
is declared as @Injectable()
but without a providedIn
context, therefore an instance will only be able to be provided if the service is listed in a providers
array somewhere (in an NgModule
, a standalone component/service etc)TestServiceProvidingModule
includes TestService
in its providers array. A single instance of TestService
is therefore available for creation. Note: The actual instance of TestService
will only be created if any component/child component in the component tree injects TestService
as a dependency.AppModule
imports TestServiceProvidingModule
. AppModule
now has access to the single instance of TestService
which originates from TestServiceProvidingModule
Note: At this point you wouldn't see 'TestService loaded' displayed to the console because no component or service has injected TestService
in its constructor. There is therefore no need for a concrete instance of TestService
to be created.
We then create a child component called ChildComponent
and inject TestService
into its constructor
app.module.ts
@NgModule({
imports: [
TestServiceProvidingModule,
ChildComponent,
],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
@Component({
selector: 'app-child',
standalone: true,
imports: [],
template: `<p>Child</p>`,
})
export class ChildComponent {
constructor(private testService: TestService) {}
}
Note the empty imports
array in ChildComponent
. The only place ChildComponent
can receive dependencies is from an ancestor
What happens here is:
TestService
dependency, the injection resolver first checks the component's providers and imports. TestService
is neither declared in this component's providers
array, nor in a module it importsAppModule
which imports a module TestServiceProviderModule
, which is able to provide a concrete instance of TestService
TestService
is created and injected into ChildComponent
The same would be true if we created a GrandchildComponent
and moved the dependency injection of TestService
from ChildComponent
to GrandchildComponent
grandchild.component.ts
@Component({
selector: 'app-grandchild',
standalone: true,
imports: [],
template: `<p>Grandchild</p>`,
})
export class GrandchildComponent {
constructor(private testService: TestService) {}
}
child.component.ts
@Component({
selector: 'app-child',
standalone: true,
imports: [GrandchildComponent],
template: `
<p>Child</p>
<app-grandchild></app-grandchild>
`,
})
export class ChildComponent {
constructor() {}
}
The same module traversal will take place, and GrandchildComponent
will ultimately receive the instance of TestService
that is provided by TestServiceProvidingModule
and made available to any components (and all descendants) declared in AppModule
's declarations
array
Now for the bit that ties is all together:
If we injected TestService
into both ChildComponent
and GrandchildComponent
, we would still only see 'TestService loaded' shown in the console once. This is because it is the same instance created in TestServiceProvidingModule
that is being injected into both child components
Upvotes: 1