I have some Angular services that store state, and I’d like these to be scoped to the appropriate component (and its subcomponents) to avoid leaking state between different instances of the same component. As there can be many services that exist in the same scope, and some are only referenced in lazy components, I’d like to specify the scope in the service rather than the scoping component.
The current two options I’m aware of are as follows:
Root provider
@Injectable({ providedIn: 'root' }) class FirstService {} @Injectable({ providedIn: 'root' }) class SecondService {} @Component({ template: `@defer { <lazy-component /> }` imports: [LazyComponent], }) class MyComponent { // same service instance for all MyComponent instances field = inject(FirstService) } @Component({}) class LazyComponent { // same service instance for all LazyComponent instances service = inject(SecondService); }
This causes leaks between component instances, and is not tree-shakable for services not loaded in the first bundle.
Explicit component provider
@Injectable() class FirstService {} @Injectable() class SecondService {} @Component({ template: `@defer { <lazy-component /> }` imports: [LazyComponent], providers: [FirstService, SecondService], }) class MyComponent { // new service instance for each MyComponent instance field = inject(FirstService) } @Component({}) class LazyComponent { // new service instance for each MyComponent instance service = inject(SecondService); }
This is correctly separated, but it requires listing every service, and all services are loaded, even if they are not needed, or first needed in a lazy-loaded module.
Ideal solution
@Injectable({ scope: 'modal' }) class FirstService {}
@Injectable({ scope: 'modal' }) class SecondService {}
@Component({
template: `@defer { <lazy-component /> }`
providerScopes: ['modal'],
imports: [LazyComponent],
}) class MyComponent {
// new service instance for each MyComponent instance
service = inject(FirstService)
}
@Component({}) class LazyComponent {
// new service instance for each MyComponent instance
service = inject(SecondService);
}
This way the services are scoped correctly, but also not listed in the service, and so can be lazy-loaded by child components.
Is there a way to achieve this?
I have some Angular services that store state, and I’d like these to be scoped to the appropriate component (and its subcomponents) to avoid leaking state between different instances of the same component. As there can be many services that exist in the same scope, and some are only referenced in lazy components, I’d like to specify the scope in the service rather than the scoping component.
The current two options I’m aware of are as follows:
Root provider
@Injectable({ providedIn: 'root' }) class FirstService {} @Injectable({ providedIn: 'root' }) class SecondService {} @Component({ template: `@defer { <lazy-component /> }` imports: [LazyComponent], }) class MyComponent { // same service instance for all MyComponent instances field = inject(FirstService) } @Component({}) class LazyComponent { // same service instance for all LazyComponent instances service = inject(SecondService); }
This causes leaks between component instances, and is not tree-shakable for services not loaded in the first bundle.
Explicit component provider
@Injectable() class FirstService {} @Injectable() class SecondService {} @Component({ template: `@defer { <lazy-component /> }` imports: [LazyComponent], providers: [FirstService, SecondService], }) class MyComponent { // new service instance for each MyComponent instance field = inject(FirstService) } @Component({}) class LazyComponent { // new service instance for each MyComponent instance service = inject(SecondService); }
This is correctly separated, but it requires listing every service, and all services are loaded, even if they are not needed, or first needed in a lazy-loaded module.
Ideal solution
@Injectable({ scope: 'modal' }) class FirstService {}
@Injectable({ scope: 'modal' }) class SecondService {}
@Component({
template: `@defer { <lazy-component /> }`
providerScopes: ['modal'],
imports: [LazyComponent],
}) class MyComponent {
// new service instance for each MyComponent instance
service = inject(FirstService)
}
@Component({}) class LazyComponent {
// new service instance for each MyComponent instance
service = inject(SecondService);
}
This way the services are scoped correctly, but also not listed in the service, and so can be lazy-loaded by child components.
Is there a way to achieve this?
Share Improve this question edited Nov 20, 2024 at 17:22 Charlie Harding asked Nov 20, 2024 at 16:04 Charlie HardingCharlie Harding 6858 silver badges24 bronze badges 4- 1 The way I would do this is with route providers. – Matthieu Riegler Commented Nov 20, 2024 at 16:19
- @MatthieuRiegler I’m not using Angular router, because I’m using Angular Elements and don’t have multiple pages. – Charlie Harding Commented Nov 20, 2024 at 16:21
- Then you could go with component provided services ? It's a bit more verbose I agree. – Matthieu Riegler Commented Nov 20, 2024 at 16:29
- @MatthieuRiegler That’s the second option I’ve given, but apart from being verbose, it also means that all classes scoped for a given component have to be loaded by that given component, even if they’re only used in the lazy child of that component. – Charlie Harding Commented Nov 20, 2024 at 16:34
1 Answer
Reset to default 0The providedIn
property accepts the following inputs.
@Injectable ({
providedIn?: Type<any> | "root" | "platform" | "any" | null | undefined;
})
In the providedIn
property you can also specify Type<any>
, which will be the component you want to scope the service to. But you can only scope it to a single component using this method.
import { Injectable } from '@angular/core';
import { LazyComponent, MyComponent } from './main';
@Injectable({
providedIn: MyComponent,
})
export class FirstService {
test = '';
}
@Injectable({
providedIn: LazyComponent,
})
export class SecondService {
test = '';
}
But this does not mean it is included in the application, to ensure it is findable by dependency injection, we can add it to the root providers array or root module providers array. But the scope defined in providedIn
scopes it to that particular component.
bootstrapApplication(App, {
providers: [FirstService, SecondService],
});
We can still import this service on other components, but it's guaranteed that the component you scoped it to has a separate instance.
Full Code:
services:
import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { FirstService, SecondService } from './services';
@Component({
selector: 'lazy-component',
standalone: true,
imports: [FormsModule],
template: `
<input [(ngModel)]="service.test"/>
{{service.test}}
`,
})
export class LazyComponent {
service = inject(SecondService);
}
@Component({
selector: 'my-component',
standalone: true,
imports: [LazyComponent],
template: `@defer { <lazy-component /> }`,
})
export class MyComponent {
service = inject(FirstService);
}
@Component({
selector: 'app-root',
imports: [FormsModule, MyComponent],
standalone: true,
template: `
<my-component/>
<input [(ngModel)]="service.test"/>
{{service.test}}
`,
})
export class App {
service = inject(FirstService);
name = 'Angular';
}
bootstrapApplication(App, {
providers: [FirstService, SecondService],
});
Stackblitz Demo
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742346456a4426641.html
评论列表(0条)