Skip to content
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

get access to aurelia container in decorators #57

Closed
devmondo opened this issue Oct 29, 2015 · 26 comments
Closed

get access to aurelia container in decorators #57

devmondo opened this issue Oct 29, 2015 · 26 comments

Comments

@devmondo
Copy link

we are facing this issue, we are using decorators for example to get instance event aggregater or some shared singleton config class without using inject, but we cant get access to Aurelia container.

we dont want to create new container, because we want to access the resolved instances of the Aurelia main container, unless there is a way for this new container to get access to Aurelia main one?

@jdanyow
Copy link
Contributor

jdanyow commented Oct 29, 2015

yep:

import {Container} from 'aurelia-dependency-injection';

// access root container
let container = Container.instance;

@stoffeastrom
Copy link

Also, if it can't resolve in the current container it will check the parent until it reaches the root container if I don't remember wrong.

@devmondo
Copy link
Author

thx @jdanyow , but i get Error: Cannot read property 'get' of undefined

maybe i am doing it wrong

import {Aurelia,inject} from 'aurelia-framework';
import {Container} from 'aurelia-dependency-injection';
import {EventAggregator} from 'aurelia-event-aggregator';



export function toolBarItem(title, eventName, cssClass) {
    return (target)=> {
        let container = Container.instance;
        let aurelia = container.get(Aurelia);

    }
}

@stoffeastrom
Copy link

When is this code executed?

@devmondo
Copy link
Author

this is part of an aurelia plugin it is a decorator to decorate a class inside that plugin

@stoffeastrom
Copy link

So the file is actually imported before Aurelia bootstrapped.. ?

I guess you have to use the config object sent into the configure plugin method and configure the module with aurelia / container.

That's what I can think of right now...

@stoffeastrom
Copy link

@devmondo
Copy link
Author

@stoffeastrom yeah you are right, the decorator function is imported before bootstrap, in fact it is imported before the configure happens, that is why i tried makeGlobal() before and it i did not work.

so still no solution

thanks by the way for the prompt reply :)

@stoffeastrom
Copy link

What happens if you move the import into the file for you plugin

export function configure(config) {
  config.globalResources("./your-plugin");
}

your-plugin.js
import "./tool-bar-item";

@devmondo
Copy link
Author

maybe i am wrong, i tried it but it does not work because globlaizeResources works only for custom elements and attributes.

my case is that the class i want to decorate is not custom element or attribute.

@stoffeastrom
Copy link

Aha.. try config.plugin("./your-plugin")?

@devmondo
Copy link
Author

still no good :(

@stoffeastrom
Copy link

Ok, I had to test my theory and I can get it to work as I said. I made a plunker for you that log out the aurelia instance. It's actually quite a cool plunker since it shows how we can use gists as plugins 💥

gist

@devmondo
Copy link
Author

thanks a lot man for the effort :) , and i get what you mean, but still it is not what i want, first i dont want to use dynamic system imports, secondly this means we have to do it for every class, and also this still dont fix the decorator function if i am correct!, i really think there should be a better way, i should be able to access the container anywhere and anytime.

but again i really appreciate the time you took :)

@stoffeastrom
Copy link

Can you post some more code? I just wanted to show that you have to load module with the decorator function in the configure method. As I said earlier only way is to some how configure the module from the configure method.

@devmondo
Copy link
Author

thanks man,

first maybe i am wrong. but if you used dynamic system import inside the plugin, it will make path relative to main app, which is not right, unless there is something i have to configure. i see that you added some modifications in the config.js for this to work, but in our case we cant, once someone install the plugin he needs not to modify anything. so System.import("./items/item1"); will resolve to ./dist/items/item1 while it should resolve to myPlugin/items/item1

another thing is that we gonna have nested usage so importing everything in config function is not practical.

let me give you an example of myplugin

index.js

export function configure(aurelia, cb) {

    //configure myPlugin 
    let instance = Container.instance.get(myPlugin );
    // Do we have a callback function?
    if (cb !== undefined && typeof(cb) === 'function') {
        cb(instance)
    }
    else {
        instance.start();
    }

}

myPlugin.js

export class myPlugin(){

start() {
        let loadedItem = Container.instance.get(item1);
            loadedItem.ea.subscribe(`${eventName}`, payLoad=> {
            loadedItem.execute(payLoad);
        });
      }
}

toolBarItem.js

export function toolBarItem() {
    return (target)=> {
        let container = Container.instance; // of course this is undefined now:(
        let ea = container.get(EventAggregater);
             target.prototype.ea = ea;
    }
}

item1.js

@toolBarItem()
export class Item1{

execute(){
//i should have this.ea as event aggegater here
}
}

@stoffeastrom
Copy link

Maybe this plunker example can help you out?

gist

@devmondo
Copy link
Author

thanks again man, but still not working , Container is always undefined :(
maybe it is this line?
https://gist.github.com/stoffeastrom/f339b7d1f196f3945c40#file-index-js-L3

i noticed you return the decorator as plugin, but really that is not the entry point, the decorator will be applied to classes that myPlugin will load as in my example above, but i tried yours and mine and both did not work

@stoffeastrom
Copy link

I don't get it :). It works in my sample. The problem is that you can't load the item1.js which loadstoolbarItem.js before the app is bootstrapped. In the code above I can't see where this is done?

@stoffeastrom
Copy link

I think you need to recreate in a skeleton-navigation app to be able to understand what you are doing. It feels like this should be possible to do.

@jdanyow
Copy link
Contributor

jdanyow commented Oct 30, 2015

Agree- @devmondo please setup a plunker or skeleton fork so @stoffeastrom can see what you're seeing.

@devmondo
Copy link
Author

@stoffeastrom @jdanyow , i get your point, and i can make it work fine like you mentioned, so you example works!, but it breaks as soon as user create items in app it self and pass it to plugin config, because as you said aurelia is not bootstrapped yet.so i either extract the decorator to another repo and init it before plugin start or disable ability for users to add items.

@devmondo
Copy link
Author

devmondo commented Nov 5, 2015

Guys, there is something really weird, i just nuked jspm folder and installed Aurelia again,and i can get access to Container instance in decorator without any of the suggested solutions above, just plain Container.instance !!!, this is amazing and strange for me at same time.

@EisenbergEffect
Copy link
Contributor

@devmondo Container.instance will be available as soon as the constructor function of the Aurelia object has completed running, which will be before your framework configuration method is run. So, it should work provided that nothing runs your module code prior to that. Another caveat is that this won't work as expected if you have more than one Aurelia app running in the same page. So....it should work but be careful....

@devmondo
Copy link
Author

devmondo commented Nov 5, 2015

@EisenbergEffect thanks for the tips, but i really did not change or add anything to my code, just updated aurelia, and it worked.

regarding that it might not work, what is your suggestion to a better approach to inject things then in decorators then ?

@EisenbergEffect
Copy link
Contributor

Don't use the DI inside a decorator. Decorators are a means of doing metaprogramming and run as part of the definition of the class. So, technically, they should be able to run without any application state.

Alternatively, you could use the decorator to store some metadata and then have the DI, as part of the object creation process, locate that metadata and then use it to affect the instance that was created...or else create some other runtime support built on top of the metadata.

You could also just create a singleton instance of the EA or other service manually and reference that in your decorator. Then during app startup, you could manually register that in the DI as a singleton so that the same instance was available through that channel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants