I have an odd requirement. Our's is a single-spa application. In which, the container app would give us a navigation menu on the left (Tree View), and we would create a new instance of the module and component and load it basing on the navigation item the user selects.
We are introducing a new feature called multi-site, wherein, users could connect to multiple databases using a single UI. So, now the left nav would show the available sites and when he expands a site, the list of navigation items would load. When user clicks on a item, we would create a instance of the component and present it to the user. For components, creating multiple instances one for each site is done by the Angular DI itself since we would open each component in tabs. The siteId would be injected into the components while creating the instance.
Whereas the problem is with the Services. As we could place data and business logic in them in observables, etc., I would need to create different instances of the same service for each site.
Is there a way, I could create multiple instances one for each site and use then in the components?
Currently, I have created a simple ServiceManger for each service which would contain a getInstance
method, which would create a instance for a service if it doesn't exist and send it back.
@Injectable({ providedIn: 'root' })
// Maintains instances of family service
export class TestServiceManager {
private readonly api = inject(TestApi);
private readonly stateInstances = new Map<string, TestServiceManager>();
/**
* Gets an instance stored with instanceName param
* @param instanceName
*/
getInstance(instanceName: string): TestService {
if (!this.stateInstances.has(instanceName)) {
this.stateInstances.set(instanceName, new TestService(this.api));
}
return this.stateInstances.get(instanceName) as TestService;
}
}
In the components, I would inject the TestServiceManager instead of the TestService, and I would access the service as below.
this.testServiceManager.getInstance(this.siteId)
I do not like this for a couple of reasons.
That being said,
Is there a better way of achieving this? I would like to see if I could let angular take care of creating different instances of services for each site basing on some custom provider or something.
If not, could we improve this and create a generic ServiceManager class using which I would not have a need to create a new ServiceManager for each service.
I was able to create a simple custom injector service and achieved this. On demand, I will create an instance of a service and return it. The created instances will be maintained in a Map so that they can be cached instead of creating a new instance for the same context on every request.
Here is the repository with the sample code.
Here is the Service factory file that creates the instance.
We could improve this even further. Currently, the cache key contains the entire service as string. Instead, we can create an md5 hash out of it and use it as key to improve performance.