Get access to aurelia root container in decorators

We are converting a plugin in from aurelia 1 to aurelia 2. We need to access the aurelia root container instance in a decorator. The container is using for getting the instance of another components. we want to access the resolved instances of the aurelia main container, without using the inject method. In the prior version we have accessed the instance by following method

    import {Container} from 'aurelia-dependency-injection';
let container = (Container.instance || new Container());
2 Likes

Hi @vipinrt, that infra is there, but the API is not there yet for friendly public consumption. For example, it can be accessed like this:

// if you are using the `aurelia` umbrella package

const aurelia = Aurelia.app(...);
const rootContainer = aurelia.container;

// if you are not using the said package
const aurelia = new Aurelia();
const rootContainer = aurelia.container;

Another way would be to grab the container instance while registering the plugin. Typically the plugin configuration object has a register(container: IContainer) method. The container can be grabbed can cached there as well.

However, note that if you have multiple "root"s in you app, you can technically have multiple instances of Aurelia to hydrate the DOM. So in that sense, there is no one true “root” container.

Moreover, let me also point out here that the lack of API is not accident IMO. I believe there are better ways to do things in Au2, which is why the static API is bit sparse in Au2. IMO having static API is bit problematic for testing.

In that light, if you can be more specific about what you want to achieve, I am sure the community members can come up with way better ideas.

Have fun with Au2 :slight_smile:

5 Likes

Thanks for your response. We are trying to convert a plugin from Au1 to Au2. We are creating the aurelia plugins using some third party javascript plugins. This decorator is used for generating the bindable properties dynamically into a custom attribute. The core javascript plugin supports certain properties, we need to create the corresponding bindable properties dynamically.Please provide a solution for converting this decorator into Au2

We have tried the below code but it’s not giving the existing container instance. (Please check the image)

    constructor(private container: IContainer) {
    console.log(container); 
    
    const aurelia = new Aurelia();
    const rootContainer = aurelia.container;
    console.log(rootContainer);        
}

This is the code block used in decorator(Au1)

import {BindableProperty, HtmlBehaviorResource} from 'aurelia-templating';
import {Container} from 'aurelia-dependency-injection';
import {metadata} from 'aurelia-metadata';
import {TaskQueue} from 'aurelia-task-queue';
import { bindingMode, BindingEngine } from 'aurelia-binding';
import { Util } from './util';

export function generateBindables(controlName, inputs, twoWayProperties, abbrevProperties, observerCollection):any {
  return function(target, key, descriptor) {
    let behaviorResource = metadata.getOrCreateOwn(metadata.resource, HtmlBehaviorResource, target);
    let container = (Container.instance || new Container());
    let util = container.get(Util);
    let bindingInstance = container.get(BindingEngine);
    inputs.push('options');
    inputs.push('widget');
    let len = inputs.length;
    if (observerCollection) {
      target.prototype.arrayObserver = [];
      observerCollection.forEach((element) => {
        target.prototype.arrayObserver.push(util.getBindablePropertyName(element));
      });
      target.prototype.bindingInstance = bindingInstance;
    }
    target.prototype.controlName = controlName;
    target.prototype.twoWays = twoWayProperties ? twoWayProperties : [];
    target.prototype.abbrevProperties = abbrevProperties ? abbrevProperties : [];
    if (len) {
      target.prototype.controlProperties = inputs;
      for (let i = 0; i < len; i++) {
        let option = inputs[i];
        if (abbrevProperties && option in abbrevProperties) {
          option = abbrevProperties[option];
          option.forEach((prop) => {
            registerProp(util, prop, target, behaviorResource, descriptor);
          });
        } else {
          registerProp(util, option, target, behaviorResource, descriptor);
        }
      }
    }
  };
}

function registerProp(util, option, target, behaviorResource, descriptor) {
  let nameOrConfigOrTarget = {
      name: util.getBindablePropertyName(option),
      defaultBindingMode:null
  };
 
  let prop = new BindableProperty(nameOrConfigOrTarget);
  prop.registerWith(target, behaviorResource, descriptor);
}

usage
@generateBindables('DatePicker', ['height', 'width', 'cssClass', 'dateFormat'],[],[],[])

We can’t find matching following components in Au2

import {BindableProperty, HtmlBehaviorResource} from 'aurelia-templating';

Also can’t find matching method for “getOrCreateOwn” in Au2

let behaviorResource = metadata.getOrCreateOwn(metadata.resource, HtmlBehaviorResource, target);

2 Likes

I have skimmed through your code. I have the following suggestion from my initial understanding of the code.

As you are writing a “plugin” you might want to register your custom elements exposed by your plugin with the container. With that respect, you can have a static method in your component class like this:

@generateBindables('MyAwesomeElement', ['foo', 'bar'],...) //<-- (L1) (see Note#1 below)
class MyAwesomeElement {
  public static register(container: IContainer): IContainer {
    // considering Util is already registered.
    const util = container.get(Util);

    // create the bindable instruction for this class here
    const bindableInstructions = Bindable.for(this); //<--- (L3) (see Note#2 below)
    for (const input of inputs) { //<--- (L2) (see Note#1 below)
       // do your thing here to get the property/attribute etc.
       bindableInstructions.add({ property,  attribute,  mode  });
    }

    const definition = CustomElement.define(
      { 
        name: 'my-awesome-element', 
        template: `awesome-template` 
      }, 
      this
    );
    container.register(definition);
  }
}

You can then register the MyAwesomeElement class with the container like this container.register(MyAwesomeElement). If you are unsure about this part, please refer to my previously liked post/thread.

Note:

  1. In yourgenerateBindables decorator, you can define the metadata for your class, and then again in (L2) you can retrieve the bindables info for your type/class. You can grab the metadata implementation from the @aurelia/metdata package. The API is not yet documented, but you can learn about that from here.
  2. To learn more about how to register the bindables, you may check this.
  3. You can actually push the register method to a different function or to a base abstract class, if you want to increase the code re-usability.

I think that this approach should work for you.

4 Likes

Thanks for your response and valuable information.

2 Likes