Comments (13)
In my opinion, you should be able to define the scope of the beans/tokens. For example, Spring has a couple of different scopes for beans, where the following 3 are the most relevant:
- singleton (default): Scopes a single bean definition to a single object instance per Spring IoC container. (like typedi already does)
- prototype: Scopes a single bean definition to any number of object instances. (like requested in this issue)
- request: Scopes a single bean definition to the lifecycle of a single HTTP request; that is each and every HTTP request will have its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. (related to this feature request: typestack/routing-controllers#174)
Here are examples how prototype and request scopes could be implemented with typeid (btw. I also likes @alexproca idea):
Prototype scope
import { Service, Scope } from "typedi"
@Service({ scope: Scope.Prototype })
class HandlerX implements Handler {}
@Service({ scope: Scope.Prototype })
class HandlerY implements Handler {}
...
@Inject()
handlers: Handler[];
Request scope
Define tokens/beans
import { Service, Scope } from "typedi"
// Each request has its own TodoController instance
@Service({ scope: Scope.Request })
class TodoController {
@Inject()
todoService: TodoService;
}
// Each request has its own TodoService instance
@Service({ scope: Scope.Request })
class TodoService {
@Inject()
todoRepo: TodoRepository;
}
// All requests use the same repository instance
@Service({ scope: Scope.Singleton })
class TodoRepository {}
Initialize a new request scope
import { RequestContainer } from "typedi"
// create RequestContainer with a request context object, it inherit all tokens/beans from Container
let requestContainer = new RequestContainer( contextObject );
// get an instance for the current request
let todoController = requestScope.get( TodoController );
from typedi.
I've implemented this feature and released it in 0.6.0
:
Using service groups
You can group multiple services into single group tagged with service id or token.
For example:
// Factory.ts
export interface Factory {
create(): any;
}
// FactoryToken.ts
export const FactoryToken = new Token<Factory>("factories");
// BeanFactory.ts
@Service({ id: FactoryToken, multiple: true })
export class BeanFactory implements Factory {
create() {
console.log("bean created");
}
}
// SugarFactory.ts
@Service({ id: FactoryToken, multiple: true })
export class SugarFactory implements Factory {
create() {
console.log("sugar created");
}
}
// WaterFactory.ts
@Service({ id: FactoryToken, multiple: true })
export class WaterFactory implements Factory {
create() {
console.log("water created");
}
}
// app.ts
// now you can get all factories in a single array
const factories = Container.getMany(FactoryToken); // factories is Factory[]
factories.forEach(factory => factory.create());
Using multiple containers and scoped containers
By default all services are stored in the global service container,
and this global service container holds all unique instances of each service you have.
If you want your services to behave and store data inside differently,
based on some user context (http request for example) -
you can use different containers for different contexts.
For example:
// QuestionController.ts
@Service()
export class QuestionController {
constructor(protected questionRepository: QuestionRepository) {
}
save() {
this.questionRepository.save();
}
}
// QuestionRepository.ts
@Service()
export class QuestionRepository {
save() {
}
}
// app.ts
const request1 = { param: "question1" };
const controller1 = Container.of(request1).get(QuestionController);
controller1.save("Timber");
Container.reset(request1);
const request2 = { param: "question2" };
const controller2 = Container.of(request2).get(QuestionController);
controller2.save("");
Container.reset(request2);
In this example controller1
and controller2
are completely different instances,
and QuestionRepository
used in those controllers are different instances as well.
Container.reset
removes container with the given context identifier.
If you want your services to be completely global and not be container-specific,
you can mark them as global:
@Service({ global: true })
export class QuestionUtils {
}
And this global service will be the same instance across all containers.
from typedi.
IMHO service groups has a problem. When you want to have several implementations of the same interface, most of the cases what you want at the end is to inject all them into an array in another class without having to know concrete implementations of that interface.
In this approach, you have to import all possible implemtantions what breaks the concept of DI and IOC. Any idea about how handling this scenario? Thanks!
from typedi.
For example:
@service('handler')
class HandlerX implements Handler {...}
@service('handler')
class HandlerY implements Handler {...}
...
@Inject('handler')
handlers: Handler[];
from typedi.
For starters what about
import { Container } from "typedi";
@service('handler')
class HandlerX implements Handler {...}
@service('handler')
class HandlerY implements Handler {...}
...
let allHandlera = Container.getAll(Handler)
And afterwards we can find a suitable way to use decorators for this functionality.
from typedi.
Any plans to support such a feature in the future? Most if not all DI frameworks support this kind of injection.
from typedi.
Can you provide an example of functionality you need?
from typedi.
no there is no such way, because each token holds single unique instance of the object
from typedi.
probably this can be implemented in the future. Need to start with a design proposal, simply @service('handler')
wont work. We need to specify somehow that this service will work as an array of instances
from typedi.
I think it could be easier adding a multi
property like angular's DI: https://angular.io/api/core/FactoryProvider#example-1
from typedi.
what @steven166 says does make sense, we'll need to implement this functionality
from typedi.
I'm also looking for something like this. Maybe it could be something like "tagged services"?
import { Container, Token } from "typedi";
export const HandlerTag= new Token<Handler>();
@Service
@Reflect.metadata("tag", HandlerTag)
class HandlerX implements Handler {...}
@Service
@Reflect.metadata("tag", HandlerTag)
class HandlerY implements Handler {...}
...
let allHandlers = Container.getTaggedServices(HandlerTag)
from typedi.
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
from typedi.
Related Issues (20)
- question: How do you test your services with dependency injection? HOT 32
- fix: Constructor Injection fail in vitest/jest HOT 3
- question: adding custom proxy traps in class constructors and instances HOT 2
- fix: non-reconstructable services are erased when imported HOT 1
- question: Stage 3 decorator support (available since TS 5.0)
- question: How to debug circular dependency injections ?
- Question: Service Injection working incorrectly HOT 1
- question: is this project/repo dead? HOT 5
- question: <Implementing repository injection using typedi-typeorm.> HOT 3
- question: <Is it possible to adapt your DI to work with modules (not classes)?> HOT 1
- question: HOT 3
- fix: Error when running in bunjs runtime HOT 2
- question: what is the point of Inject if we can use Container.get
- question: Inject Private Property HOT 2
- Using ContainerInstance in the service constructor causes TypeScript build breaks HOT 1
- question: identifier was not found in the container HOT 2
- question: Using typedi with monorepo HOT 3
- question: Using Typedi with Vite - Returning ContainerInstance2 HOT 3
- question: How to define a third-party dependency HOT 5
- question: Using Token with Generics failed in stongly typying
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typedi.