New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhanced Dependency Injection Use #73
Comments
Sounds like you're looking for the equivalent of: // configure the container
container.RegisterInstance<Something>("Server A", new Something("http://foo.com"));
container.RegisterInstance<Something>("Server B", new Something("http://bar.com"));
// resolve by name
var somethingA = container.Resolve<Something>("Server A");
var somethingB = container.Resolve<Something>("Server B");
// constructor injection
public MyClass([Dependency("Server A")] Something a, [Dependency("Server B")] Something b)
{
.....
} Here's the equivalent using javascript and Aurelia's container: // configure the container
container.registerInstance("Server A", new Something("http://foo.com"));
container.registerInstance("Server B", new Something("http://bar.com"));
// resolve by name
var somethingA = container.get("Server A");
var somethingB = container.get("Server B");
// constructor injection
@inject("Server A", "Server B")
export MyClass(a, b)
{
.....
} Some notes:
Hope that helps! |
Thanks! That was helpful, however, not quite exactly what I'm looking for. So a use case that I currently just ran up against might be a better example. I have an api client class that pings the server for data, however when testing, I have a flag to change that uses mock data instead. In your example, the registering of instances sounds absolutely like the first step. However, the injection in the example still requires both to be passed in to the class. Does that all make sense? |
definitely makes sense, this is a common scenario. Here's how you would do something like that: main.js import {ApiClient} from './api-client';
import {isDebug} from './configuration';
export function configure(aurelia) {
...
...
// configure the container
let container = aurelia.container;
container.registerInstance(ApiClient, new ApiClient(isDebug));
...
aurelia.start().then(a => a.setRoot());
} inbox.js import {inject} from 'aurelia-framework';
import {ApiClient} from './api-client';
@inject(ApiClient)
export class Inbox {
constructor(api) { // instance registered at app startup will be injected here
...
}
...
} If you weren't passing a "debug" flag to your api client class and instead wanted to use a mock api client class, a naive approach would be to change the main.js import {ApiClient} from './api-client';
import {ApiClientMock} from './api-client-mock';
import {isDebug} from './configuration';
export function configure(aurelia) {
...
...
// configure the container
let container = aurelia.container;
if (isDebug) {
container.registerHandler(ApiClient, c => c.get(ApiClientMock));
}
...
aurelia.start().then(a => a.setRoot());
} This does the job, but you really don't want to mix your mock container configuration logic with your production container configuration logic. There's no need to be importing mock modules like import {isDebug} from './configuration';
export function configure(aurelia) {
...
...
// configure the container
let moduleName = isDebug ? './debug-container-config' : './release-container-config';
System.import(moduleName)
.then(module => module.configureContainer(aurelia.container))
.then(() => aurelia.start())
.then(a => a.setRoot());
} |
I think that solves our use case and issue. Thanks for the help! |
Another question for this. Back to the Api example with multiple servers. I need to have the same base api client, but depending on which module is using the api, the baseUrl of the fetch needs to be different. How can I have the modules just inject the api class, and DI know that depending on which class asked for the api, give it the one setup with the appropriate baseUrl? |
hmmm- sounds like you're looking for the equivalent of unity's var clientA = new ApiClient("http://foo.com");
var clientB = new ApiClient("http://bar.com");
container.RegisterType<ModuleA>(new InjectionConstructor(clientA));
container.RegisterType<ModuleB>(new InjectionConstructor(clientB)); if that's what you're looking for, here's how it's done with Aurelia's container: let clientA = new ApiClient("http://foo.com");
let clientB = new ApiClient("http://bar.com");
container.registerHandler(ModuleA, c => new ModuleA(clientA));
container.registerHandler(ModuleB, c => new ModuleB(clientB)); Another way of doing this would be to create a child containers and register the appropriate ApiClient instance in each of the child containers. In practice this would be pretty tricky because you'd need to make sure ModuleA was always resolved from the correct child container, same thing for ModuleB. Both of these approaches are less flexible than declaring the deps on the class using |
I think I'm going to go with the approach of the registering of two "types" of api's, thanks. For future knowledge, could you register the handler via a path? For example, any modules that exist inside of specific folder have clientA as the api? |
However, registering those classes in the main.js configure function might be a problem. The api client makes use of the HttpClient from Aurelia, and is currently injecting it. I could new up an HttpClient in the main.js configure function, but then if I use DI anywhere else with the HttpClient, to my understanding, the two won't be the same. |
get the HttpClient from the container: let http = container.get(HttpClient);
... proceed to configure the container ... |
Thanks for all the help. Still learning all the ins and outs here with containers and DI |
I have tried using the register methods as @jdanyow mentioned above but I'm guessing the interface has changed. Does registration require a key even if you don't intend to resolve by key? I'm trying to register one concretion for an interface under debug config but another concretion in all other configurations and just can't figure out how to do this.
|
@millicandavid there was a bug in the code I had shared above- I've updated it. Needs to be: ....registerHandler(IWebApiClient, c => c.get(TestApiClient)) |
Thanks Jeremy. You're a life saver. Now if I can just figure out why the typescript transpiler can't find my interface even though it is clearly imported. |
@millicandavid unfortunately you cannot use a typescript interface as a registration key. At runtime there's no interface (because interfaces don't exist in JavaScript). Try using an abstract class instead (assuming you're using the TypeScript 2.x RC). |
Thanks again Jeremy. That makes complete sense. I was just used to the pattern of using an interface there and didn't even think about it. This is all working for me now. |
Great advice @jdanyow thanks a lot.
|
@CasiOo that sounds like a great use-case for |
What is the proper way to do this with the CLI and typescript |
Hi, sorry for hijacking this, but it looks like almost what I'd like to achieve. My code base works for 2 somewhat different contexts, which share code. In one context I will have a specific Now some part of my code are referencing the Overloading the
This is in the bootstrap of my other context app. However, instead of having a proper instance object of I understand that this approach could work in my case:
But I am a bit annoyed when using this approach since the I am afraid that if I manually instantiate each of these instances to follow the So my question is, how could I do the |
@lemoustachiste I think I can make this super easy for you :) In the case above, Bear in mind that if you register two things in the container using the same key, that won't override anything. Instead, resolving the key will return the first thing that was registered and doing a |
Thanks @EisenbergEffect, it worked very smoothly and I like that :). Thanks again for the prompt answer |
Is there a way for dependency injection in Aurelia to do more than just (essentially) instantiate a class and make sure that that class is the one passed around? After talking with other developers who have used Unity in C# as DI, there seems to be so much more that DI can do than Aurelia is handling, but I could just not know as much about it in Aurelia.
So, if I had a single module that would export two different classes depending on who was asking for it, can Aurelia's DI handle that situation via some configuration?
For example, say I have an api class, and for some reason I had two different kinds of servers (server A & server B) I needed to ping depending on where it was asking, so that the only difference for this api class is the base url of the request. If I now have a class A that needs to make a request to server A, and also a class B that needs to go to server B, can I use DI here so that both of those classes (A & B) will receive the correctly configured api for servers A & B?
The text was updated successfully, but these errors were encountered: