GithubHelp home page GithubHelp logo

aurelia / dependency-injection Goto Github PK

View Code? Open in Web Editor NEW
156.0 156.0 67.0 2.48 MB

A lightweight, extensible dependency injection container for JavaScript.

License: MIT License

JavaScript 4.53% TypeScript 95.47%

dependency-injection's Introduction

Aurelia

License: MIT npm version CircleCI TypeScript Twitter

Backers on Open Collective Sponsors on Open Collective Discord Chat

Aurelia 2

This is the Aurelia 2 monorepo, containing core and plugin packages, examples, benchmarks, and documentation for the upcoming major version of everybody's favorite modern JavaScript framework, Aurelia.

Introduction

Aurelia is a modern, front-end JavaScript framework for building browser, mobile, and desktop applications. It focuses on aligning closely with web platform specifications, using convention over configuration, and having minimal framework intrusion. Basically, we want you to just write your code without the framework getting in your way. ๐Ÿ˜‰

Aurelia applications are built by composing a series of simple components. By convention, components are made up of a vanilla JavaScript or Typescript class, with a corresponding HTML template.

//app.js
export class App {
  welcome = "Welcome to Aurelia";

  quests = [
    "To seek the holy grail",
    "To take the ring to Mordor",
    "To rescue princess Leia"
  ];
}
<!-- app.html -->
<form>
  <label>
    <span>What is your name?</span>
    <input value.bind="name & debounce:500">
  </label>

  <label>
    <span>What is your quest?</span>
    <select value.bind="quest">
      <option></option>
      <option repeat.for="q of quests">${q}</option>
    </select>
  </label>
</form>

<p if.bind="name">${welcome}, ${name}!</p>
<p if.bind="quest">Now set forth ${quest.toLowerCase()}!</p>

This example shows you some of the powerful features of the aurelia binding syntax. To learn further, please see our documentation.

Feeling excited? Check out how to use makes to get started in the next section.

Note: Please keep in mind that Aurelia 2 is still in beta. A number of features and use cases around the public API are still untested and there will be a few more breaking changes.

Getting Started

First, ensure that you have Node.js v8.9.0 or above installed on your system. Next, using npx, a tool distributed as part of Node.js, we'll create a new Aurelia 2 app. At a command prompt, run the following command:

npx makes aurelia

This will cause npx to download the makes scaffolding tool, along with the aurelia generator, which it will use to guide you through the setup process. Once complete, you'll have a new Aurelia 2 project ready to run. For more information on Aurelia's use of makes, see here. If you aren't interested in taking our preferred approach to generating a project, you can also see the examples folder in this repo for pure JIT setups (no conventions) with various loaders and bundlers.

Documentation

You can read the documentation on Aurelia 2 here. Our new docs are currently a work-in-progress, so the most complete documentation is available in our getting started section. If you've never used Aurelia before, you'll want to begin with our Quick Start Guide.

Contributing

If you are interested in contributing to Aurelia, please see our contributor documentation for more information. You'll learn how to build the code and run tests, how best to engage in our social channels, how to submit PRs, and even how to contribute to our documentation. We welcome you and thank you in advance for joining with us in this endeavor.

Staying Up-to-Date

To keep up to date on Aurelia, please visit and subscribe to the official blog and our email list. We also invite you to follow us on twitter. If you have questions, have a look around our Discourse forum. For chat on Aurelia 2, join our new Aurelia 2 community on Discord. If you'd like to join the growing list of Aurelia sponsors, please back us on Open Collective.

License

Aurelia is MIT licensed. You can find out more and read the license document here.

dependency-injection's People

Contributors

0x-r4bbit avatar arfilon avatar awilczek avatar behzad888 avatar bigopon avatar bryanrsmith avatar cmichaelgraham avatar davismj avatar didehvar avatar djindjic avatar doktordirk avatar drsammyd avatar eisenbergeffect avatar gheoan avatar jbellsey avatar jdanyow avatar kyeotic avatar kylemit avatar leventebalogh avatar martingust avatar matjaz avatar niieani avatar npelletm avatar perlun avatar plwalters avatar pndewit avatar rockresolve avatar sayan751 avatar silbinarywolf avatar strahilkazlachev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dependency-injection's Issues

Indicate the name of the module if DI fails to find key

Ex
static inject() { return [WorngSpelling]; }
Returns a null / undefined error in container.js, the error should include the (incorrect) name of the failed module to load or at least the module that the load failure occurred in (currently the router swallows the stack)

Autoregister Non-functions

Right now, when an explicit registration doesn't exist and the container tries to autoregister something, it assumes it's a function. Extend the autoregister logic to register the non-function as an instance.

Improve error messages for resolution failures

Currently if one of your Aurelia views has a bad import, such as import Router from 'aurelia-framework'; (should be { Router }), the error you get is "Error: key cannot be null or undefined.". Errors like this can be tricky to track down, especially for people new to the framework. It would be awesome if the container could provide a little context in its error messages. Eg, "Unable to resolve dependency 'null' required by class 'App'. Error: Dependency key cannot be null or undefined." Similarly, if a service constructor throws an exception there is very little context provided because the container just lets the exception fly.

Work with typescript?

Hey, i was browsing some other aurelia code. You guys have a lot of cool stuff.
And was wondering what this lib is/ how to use it. And if it could be used with typescript?

Dependency Injection injecting new instance of parent class

I spoke with @EisenbergEffect yesterday (2015-05-05) about this issue.

The issue is pertaining to dependency injection and parent/child Custom Elements. I'm implementing Famo.us for Aurelia, and I'm trying to have the parent class injected into the child so that I can access its context. But when I do so, it injects a new instance of the parent class rather than the instance that was created by the view-model.

The issue is in famous-surface-aurelia.js where famousModifier is being injected into the constructor. this.famousModifier is assigned a new instance of FamousModifierCustomElement. So famous-modifier in famous-demo.jade !== this.famousModifier in famous-surface-aurelia.js.

Currently I'm using EventAggregator to communicate between parent/child, but this is obviously not very efficient as there can be many Famo.us modifiers/surfaces/etc.

If you need any more elaboration, I'll be watching this issue, or you can reach me on Gitter.

Here is a basic setup to reproduce the issue.

famous-demo.jade (the view that is routed to in the nav bar)

template
  require(from="./famous-aurelia/famous-app-aurelia")
  require(from="./famous-aurelia/famous-modifier-aurelia")
  require(from="./famous-aurelia/famous-surface-aurelia")
  section
    .row
      .col-lg-2
      .col-lg-8
        input(value.bind="isChecked")
        div(innerhtml.bind="isChecked")
        famous-app
          famous-modifier(pipe-to.bind="modifierUUID", transform="Transform.translate([200, 560, 0])")
            famous-surface(pipe-from.bind="modifierUUID", size.bind="[500, 200]", content='<h1 innerhtml.bind="message">Hello ${isChecked}, ๐ŸŒŽ!</h1>', properties.bind='{"backgroundColor": "#0AF"}')
      .col-lg-2

famous-modifier-aurelia.js

import {inject, customElement, bindable, noView} from 'aurelia-framework';
import {EventAggregator} from 'aurelia-event-aggregator';
import famous from 'famous';
import StateModifier from 'famous/modifiers';
import Transform from 'famous/core';

const DEFAULT_OPTIONS = {
  align: null,
  opacity: new famous.transitions.Transitionable(1),
  origin: null,
  size: null,
  transfrom: new famous.transitions.TransitionableTransform(Transform.identity)
};

const NAV_BAR_HEIGHT = 30; // px

@inject(EventAggregator)
@bindable('pipeTo')
@bindable('pipeFrom')
@bindable('init')
@bindable('align')
@bindable('opacity')
@bindable('origin')
@bindable('size')
@bindable('transform')
export class FamousModifierCustomElement {
  constructor(eventAggregator) {
    this.eventAggregator = eventAggregator;
    this.subscriptions = [];

    this.options = {};

    this.modifier;

    this.assigned = false;

    this.uuid = this.generateUUID();
  }

  /* Aurelia Hooks */
  attached() {
    this.parseOptions();

    this.build();

    this.subscribe();

    this.eventAggregator.publish('famous-app?', { modifier: true });
  }

  detached() {
    for (let subscription of this.subscriptions) {
      subscription(); // Call the `dispose` function.
    }
    this.modifier = () => {};
  }

  subscribe() {
    this.subscriptions.push(this.eventAggregator.subscribe('famous-modifier?', payload => {
      if (payload.pipeFrom === this.pipeTo && payload.surface) {
        this.eventAggregator.publish('famous-modifier!', { pipeTo: this.pipeTo, modifier: this.subContext });
      }
    }));

    this.subscriptions.push(this.eventAggregator.subscribe('famous-app!', payload => {
      if (payload.app && !this.assigned) {
        this.subContext = payload.app.add(this.modifier);
        this.assigned = !this.assigned;
      }
    }));
  }

  /* Custom Methods */
  build() {
    this.modifier = new StateModifier(this.options);
  }

  generateUUID() {
    var d = performance.now();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (d + Math.random()*16)%16 | 0;
        d = Math.floor(d/16);
        return (c=='x' ? r : (r&0x3|0x8)).toString(16);
    });
    return uuid;
  }

  parseOptions() {
    for (let key in DEFAULT_OPTIONS) {
      if (key !== 'transform') { // FIXME: Remove this line when done testing.
        if (this.init !== undefined && this.init[key] !== undefined) {
          this.options[key] = this.init[key];
        } else if (this[key] !== undefined) {
          if (typeof(this[key]) === 'object') {
            this.options[key] = this[key];
          } else {
            this.options[key] = JSON.parse(this[key]);
          }
        } else {
          this.options[key] = DEFAULT_OPTIONS[key];
        }
      }
    }
  }
}

famous-surface-aurelia.js

import {inject, customElement, bindable, noView} from 'aurelia-framework';
import {ObserverLocator} from 'aurelia-framework';
import {EventAggregator} from 'aurelia-event-aggregator';
import {FamousModifierCustomElement} from 'famous-aurelia/famous-modifier-aurelia'
import famous from 'famous';

const DEFAULT_OPTIONS = {
  attributes: {},
  classes: [],
  content: '',
  properties: {},
  size: [true, true]
};

@inject(EventAggregator, ObserverLocator, FamousModifierCustomElement)
@bindable('pipeTo')
@bindable('pipeFrom')
@bindable('init') // JSON object with all possible keys.
@bindable('size')
@bindable('classes')
@bindable('properties')
@bindable('attributes')
@bindable('content')
export class FamousSurfaceCustomElement {
  constructor(eventAggregator, observerLocator, famousModifier) {
    this.eventAggregator = eventAggregator;
    this.subscriptions = [];
    this.observerLocator = observerLocator;

    /* THIS LINE HERE */
    this.famousModifier = famousModifier;
    /* THIS LINE HERE */

    this.uuid = this.generateUUID();

    this.options = {};

    this.message = 'Nothing';

    this.surface;
  }

  /* Debugging Functions */

  /* Aurelia Hooks */
  attached() {
    var subscription = this.observerLocator
          .getObserver(this.parent, 'isChecked')
          .subscribe((n, o) => {
            this.message = n;
            this.surface.setContent(n);
          });

    this.parseOptions();

    this.build();

    this.subscribe();

    this.eventAggregator.publish('famous-modifier?', { pipeFrom: this.pipeFrom, surface: true });
  }

  bind(c) {
    this.parent = c;
  }

  detached() {
    for (let subscription of this.subscriptions) {
      subscription(); // Call the `dispose` function.
    }
    this.surface = () => {};
  }

  subscribe() {
    this.subscriptions.push(this.eventAggregator.subscribe('famous-modifier!', payload => {
      if (payload.pipeTo === this.pipeFrom && payload.modifier) {
        this.subContext = payload.modifier.add(this.surface);
      }
    }));
  }

  /* Custom Methods */
  build() {
    var Surface = famous.core.Surface;

    this.surface = new Surface(this.options);

    this.eventAggregator.publish('famous-surface?', payload => {
      this.surface = payload.surface;
    });
  }

  generateUUID() {
      var d = performance.now();
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
          var r = (d + Math.random()*16)%16 | 0;
          d = Math.floor(d/16);
          return (c=='x' ? r : (r&0x3|0x8)).toString(16);
      });
      return uuid;
  }

  parseOptions() {
    for (let key in DEFAULT_OPTIONS) {
      if (this.init !== undefined && this.init[key] !== undefined) {
        this.options[key] = this.init[key];
      } else if (this[key] !== undefined) {
        if (key === 'content') {
          this.options[key] = this[key];
        } else if (typeof(this[key]) === 'object') {
          this.options[key] = this[key];
        } else {
          this.options[key] = JSON.parse(this[key]);
        }
      } else {
        this.options[key] = DEFAULT_OPTIONS[key];
      }
    }
  }
}

Dependency injection as standalone library

Hi,

we are writing a library in TypeScript 1.5.
We would like to achieve dependency injection using ES7 proposal decorators.
Instead of writing it ourselves we found your DI component (aurelia-dependency-injection) and tried to use it in our enviroment.
We're faceing some problems. Did you predicted usage of aurelia-dependency-injection without entire aurelia framework?
If so, how can we do it?.. How to create objects without passing arguments to constructor?

index.html

<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
  System.import('jquery');
  System.import('scripts/Application');
</script>

scripts/SampleTest.ts

export class SampleTest {
  testName = 'Jon Doe test';
  constructor() {
    console.log('HEY 1', this);
  }
}

scripts/Application.ts

import {inject} from 'aurelia-dependency-injection';
import {SampleTest} from 'scripts/SampleTest';

@inject(SampleTest)
export class Application {
  constructor(simpletest) {
    console.log(this, simpletest); // result is: Application {},  undefined
  }
}

new Application(); // Is there other way to do this (initialize the app)?

Class Activation process hook

We need a way to subscribe to ANY classes activation. So that we can "auto" inject stuff during the activation.
With our platform as we think more abstraction and sustainabilities.
It would be great to have a such feature in the Aurelia Framework.
If I may, the big mess here is to have a BIG overview of the Entire Aurelia Architecture. This is really important for people like us as a key decision makers to trust this Framework and sale it more and more.

Lรฉon

Allow injection of parent routers without creating new Child Router

Currently, it is possible to get the parent of a router in a child viewmodel by creating a child router and referencing its parent. There is no way to directly inject the parent router.

Use case for this includes child viewmodels that may wish to call router.navigate, router.navigateBack, or other router methods on the router that owns it.

Singleton custom element viewmodel isn't singleton

I have a custom element (called Confirm) whose view model I want to be a singleton. Basically, the element is loaded in a view but then its view model will be used by other custom elements and they all need to work with the same instance of the VM.

Decorating the element with singleton() doesn't prevent the viewmodel constructor to be called twice (once when Confirm element is loaded/rendered and once when it's injected as a dependency).

//custom element code

@singleton()
export class Confirm {
      constructor() {
       console.log("confirm created");

   }
}

//other custom element using Custom

@autoinject
export class Delete {
       constructor(public confirm:Confirm) {

      // alternate approach, it doesn't work either
      // this.confirm = Container.instance.get("Confirm");

    }

//in the view
 <require from="./resources/confirm"></require>
    <confirm></confirm>
    <delete></delete>

I get "confirm created" twice. I don't know if it's a bug or it's me not knowing how to use the DI Container.

Constructor injection works only for one argument (TypeScript 1.6)

Hi,

I'm seeing an issue where Aurelia's IOC works fine for one argument, but when I try to inject 2 arguments, I get a value for the first one, but "undefined" for the second one when an instance of my class is created. Here my scenario:

I have a simple TypeScript 1.6 class (see below). When I run this and put a breakpoint in the constructor, I see that I get an instance of "httpClient" argument, but "undefined" for "authStatus".

import {inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";

import {AuthStatus, AuthInfo} from "../Security/user-auth";

@inject(HttpClient)
@inject(AuthStatus)
export class QuickAdd {
    constructor(httpClient: HttpClient, authStatus: AuthStatus) {
        ...
    }

    ...

Here the generated JS code from the TS compiler:

/// <reference path="../wwwroot/jspm_packages/github/aurelia/[email protected]/aurelia-framework.d.ts" />
/// <reference path="../wwwroot/jspm_packages/github/aurelia/[email protected]/aurelia-http-client.d.ts" />
System.register(["aurelia-framework", "aurelia-http-client", "../Security/user-auth"], function(exports_1) {
    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc);
        switch (arguments.length) {
            case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
            case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0);
            case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc);
        }
    };
    var aurelia_framework_1, aurelia_http_client_1, user_auth_1;
    var QuickAdd;
    return {
        setters:[
            function (aurelia_framework_1_1) {
                aurelia_framework_1 = aurelia_framework_1_1;
            },
            function (aurelia_http_client_1_1) {
                aurelia_http_client_1 = aurelia_http_client_1_1;
            },
            function (user_auth_1_1) {
                user_auth_1 = user_auth_1_1;
            }],
        execute: function() {
            QuickAdd = (function () {
                function QuickAdd(httpClient, authStatus) {
                    ...
                }
                QuickAdd = __decorate([
                    aurelia_framework_1.inject(aurelia_http_client_1.HttpClient),
                    aurelia_framework_1.inject(user_auth_1.AuthStatus)
                ], QuickAdd);
                return QuickAdd;
            })();
            exports_1("QuickAdd", QuickAdd);
        }
    }
});

First I thought I'm doing something wrong as HttpClient is an Aurelia FW class whereas AuthStatus is a class I implemented. But what's interesting is the following: If I remove the HttpClient argument (and the corresponding "@Inject(HttpClient)" line) then I get an instance of AuthStatus for "authStatus".
Adding the HttpClient parameter and related code back and I only get a HttpClient instance and "undefined" for AuthStatus. IOW the following works:

import {inject} from "aurelia-framework";

import {AuthStatus, AuthInfo} from "../Security/user-auth";

@inject(AuthStatus)
export class QuickAdd {
    constructor(authStatus: AuthStatus) {
        ...
    }

    ...

I tried to debug it a bit and the following caught my attention (in aurelia-dependency-injection.js). I was expecting that metadata information for the QuickAdd constructor would contain an array of 2 keys (one for each argument). Instead I only see one for HttpClient.

screenshot1

I'm new to Aurelia and I don't really know the code base, but I thought I should mention it.

Any idea what I'm doing wrong?

Something that may be related: As mentioned above DI works fine if I have 1 argument. But I noticed that if I use "@autoinject()", then the constructor parameter is "undefined" (even if I only use a single argument in the constructor - no matter whether I use HttpClient or AuthStatus). If I replace "@autoinject()" with "@Inject(ClassName)" for the argument I'm trying to inject into the constructor, then it works fine.

My environment:

  • TypeScript 1.6
  • I use JSPM with Babel
  • aurelia-framework 0.16.0
  • Using Chrome as browser

Dependency injection fails if using push state

Everything works file as long as I only keep one level in my URLs. As soon as I go deeper into something like this:
http://localhost:9000/-JkGelqaJ0h6t81nsrAQ/add/

I can no longer refresh the page without getting a bunch of errors.

I changed to using the history API (push state) instead of URL hashes as per the instructions here: http://aurelia.io/docs.html#configuring-push-state

You will notice in this error that Aurelia is using the add/ "directory" as its base URL for trying to access dependencies.

GET http://localhost:9000/-JkGelqaJ0h6t81nsrAQ/add/jspm_packages/github/aurelia/[email protected] 404 (Not Found)
es6-module-loader.src.js:139Potentially unhandled rejection [3] Error loading "github:aurelia/[email protected]" at http://localhost:9000/-JkGelqaJ0h6t81nsrAQ/add/jspm_packages/github/aurelia/[email protected]
Not Found: http://localhost:9000/-JkGelqaJ0h6t81nsrAQ/add/jspm_packages/github/aurelia/[email protected] (WARNING: non-Error used)

Here's my config.js, I'm guessing that something needs to be adjusted there.

System.config({
  "paths": {
    "*": "*.js",
    "github:*": "jspm_packages/github/*.js",
    "npm:*": "jspm_packages/npm/*.js",
    "aurelia-skeleton-navigation/*": "lib/*.js",
    "budget/*": "lib/*.js"
  },
  "baseUrl": "dist"
});

System.config({
  "map": {
    "aurelia-bootstrapper": "github:aurelia/[email protected]",
    "aurelia-dependency-injection": "github:aurelia/[email protected]",
    "aurelia-framework": "github:aurelia/[email protected]",
    "aurelia-http-client": "github:aurelia/[email protected]",
    "aurelia-router": "github:aurelia/[email protected]",
    "bootstrap": "github:twbs/[email protected]",
    "firebase": "npm:[email protected]",
    "font-awesome": "npm:[email protected]",
    "github:aurelia/[email protected]": {
      "aurelia-dependency-injection": "github:aurelia/[email protected]",
      "aurelia-metadata": "github:aurelia/[email protected]",
      "aurelia-task-queue": "github:aurelia/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-event-aggregator": "github:aurelia/[email protected]",
      "aurelia-framework": "github:aurelia/[email protected]",
      "aurelia-history": "github:aurelia/[email protected]",
      "aurelia-history-browser": "github:aurelia/[email protected]",
      "aurelia-loader-default": "github:aurelia/[email protected]",
      "aurelia-logging-console": "github:aurelia/[email protected]",
      "aurelia-router": "github:aurelia/[email protected]",
      "aurelia-templating": "github:aurelia/[email protected]",
      "aurelia-templating-binding": "github:aurelia/[email protected]",
      "aurelia-templating-resources": "github:aurelia/[email protected]",
      "aurelia-templating-router": "github:aurelia/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-metadata": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-binding": "github:aurelia/[email protected]",
      "aurelia-dependency-injection": "github:aurelia/[email protected]",
      "aurelia-loader": "github:aurelia/[email protected]",
      "aurelia-logging": "github:aurelia/[email protected]",
      "aurelia-metadata": "github:aurelia/[email protected]",
      "aurelia-task-queue": "github:aurelia/[email protected]",
      "aurelia-templating": "github:aurelia/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-history": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-path": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-loader": "github:aurelia/[email protected]",
      "aurelia-metadata": "github:aurelia/[email protected]",
      "aurelia-path": "github:aurelia/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-html-template-element": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]",
      "webcomponentsjs": "github:webcomponents/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-dependency-injection": "github:aurelia/[email protected]",
      "aurelia-event-aggregator": "github:aurelia/[email protected]",
      "aurelia-history": "github:aurelia/[email protected]",
      "aurelia-path": "github:aurelia/[email protected]",
      "aurelia-route-recognizer": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-binding": "github:aurelia/[email protected]",
      "aurelia-templating": "github:aurelia/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-binding": "github:aurelia/[email protected]",
      "aurelia-dependency-injection": "github:aurelia/[email protected]",
      "aurelia-logging": "github:aurelia/[email protected]",
      "aurelia-templating": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-dependency-injection": "github:aurelia/[email protected]",
      "aurelia-metadata": "github:aurelia/[email protected]",
      "aurelia-path": "github:aurelia/[email protected]",
      "aurelia-router": "github:aurelia/[email protected]",
      "aurelia-templating": "github:aurelia/[email protected]"
    },
    "github:aurelia/[email protected]": {
      "aurelia-binding": "github:aurelia/[email protected]",
      "aurelia-dependency-injection": "github:aurelia/[email protected]",
      "aurelia-html-template-element": "github:aurelia/[email protected]",
      "aurelia-loader": "github:aurelia/[email protected]",
      "aurelia-logging": "github:aurelia/[email protected]",
      "aurelia-metadata": "github:aurelia/[email protected]",
      "aurelia-path": "github:aurelia/[email protected]",
      "aurelia-task-queue": "github:aurelia/[email protected]",
      "core-js": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "assert": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "buffer": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "constants-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "crypto-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "events-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "Base64": "npm:[email protected]",
      "events": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "stream": "github:jspm/[email protected]",
      "url": "github:jspm/[email protected]",
      "util": "github:jspm/[email protected]"
    },
    "github:jspm/[email protected]": {
      "https-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "os-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "path-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "process": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "stream-browserify": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "url": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "util": "npm:[email protected]"
    },
    "github:jspm/[email protected]": {
      "vm-browserify": "npm:[email protected]"
    },
    "github:systemjs/[email protected]": {
      "clean-css": "npm:[email protected]",
      "fs": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]"
    },
    "github:twbs/[email protected]": {
      "css": "github:systemjs/[email protected]",
      "jquery": "github:components/[email protected]"
    },
    "npm:[email protected]": {
      "fs": "github:jspm/[email protected]",
      "module": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "asn1.js": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "assert": "github:jspm/[email protected]",
      "bn.js": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "minimalistic-assert": "npm:[email protected]",
      "vm": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "util": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "create-hash": "npm:[email protected]",
      "crypto": "github:jspm/[email protected]",
      "fs": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "stream": "github:jspm/[email protected]",
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "constants": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "constants": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "randombytes": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "browserify-rsa": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "elliptic": "npm:[email protected]",
      "inherits": "npm:[email protected]",
      "parse-asn1": "npm:[email protected]",
      "stream": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "base64-js": "npm:[email protected]",
      "ieee754": "npm:[email protected]",
      "is-array": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "commander": "npm:[email protected]",
      "fs": "github:jspm/[email protected]",
      "http": "github:jspm/[email protected]",
      "https": "github:jspm/[email protected]",
      "os": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]",
      "source-map": "npm:[email protected]",
      "url": "github:jspm/[email protected]",
      "util": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "child_process": "github:jspm/[email protected]",
      "events": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "elliptic": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "fs": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "ripemd160": "npm:[email protected]",
      "sha.js": "npm:[email protected]",
      "stream": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "create-hash": "npm:[email protected]",
      "crypto": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "stream": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "browserify-aes": "npm:[email protected]",
      "browserify-sign": "npm:[email protected]",
      "create-ecdh": "npm:[email protected]",
      "create-hash": "npm:[email protected]",
      "create-hmac": "npm:[email protected]",
      "diffie-hellman": "npm:[email protected]",
      "inherits": "npm:[email protected]",
      "pbkdf2-compat": "npm:[email protected]",
      "public-encrypt": "npm:[email protected]",
      "randombytes": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "miller-rabin": "npm:[email protected]",
      "process": "github:jspm/[email protected]",
      "randombytes": "npm:[email protected]",
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "brorand": "npm:[email protected]",
      "hash.js": "npm:[email protected]",
      "inherits": "npm:[email protected]",
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "fs": "github:jspm/[email protected]",
      "http": "github:jspm/[email protected]",
      "https": "github:jspm/[email protected]",
      "net": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]",
      "stream": "github:jspm/[email protected]",
      "tls": "github:jspm/[email protected]",
      "url": "github:jspm/[email protected]",
      "util": "github:jspm/[email protected]",
      "websocket-driver": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "faye-websocket": "npm:[email protected]",
      "http": "github:jspm/[email protected]",
      "https": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "inherits": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "http": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "util": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "brorand": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "os": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "asn1.js": "npm:[email protected]",
      "asn1.js-rfc3280": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "pemstrip": "npm:[email protected]",
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "asn1.js": "npm:[email protected]",
      "browserify-aes": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "create-hash": "npm:[email protected]",
      "pbkdf2-compat": "npm:[email protected]",
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "child_process": "github:jspm/[email protected]",
      "create-hmac": "npm:[email protected]",
      "crypto": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]",
      "systemjs-json": "github:systemjs/[email protected]"
    },
    "npm:[email protected]": {
      "bn.js": "npm:[email protected]",
      "browserify-rsa": "npm:[email protected]",
      "buffer": "github:jspm/[email protected]",
      "create-hash": "npm:[email protected]",
      "crypto": "github:jspm/[email protected]",
      "parse-asn1": "npm:[email protected]",
      "randombytes": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "core-util-is": "npm:[email protected]",
      "events": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "isarray": "npm:[email protected]",
      "process": "github:jspm/[email protected]",
      "stream": "npm:[email protected]",
      "stream-browserify": "npm:[email protected]",
      "string_decoder": "npm:[email protected]",
      "util": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "fs": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "amdefine": "npm:[email protected]",
      "fs": "github:jspm/[email protected]",
      "path": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "events": "github:jspm/[email protected]",
      "inherits": "npm:[email protected]",
      "readable-stream": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "assert": "github:jspm/[email protected]",
      "punycode": "npm:[email protected]",
      "querystring": "npm:[email protected]",
      "util": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "inherits": "npm:[email protected]",
      "process": "github:jspm/[email protected]"
    },
    "npm:[email protected]": {
      "indexof": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "buffer": "github:jspm/[email protected]",
      "crypto": "github:jspm/[email protected]",
      "events": "github:jspm/[email protected]",
      "net": "github:jspm/[email protected]",
      "process": "github:jspm/[email protected]",
      "stream": "github:jspm/[email protected]",
      "url": "github:jspm/[email protected]",
      "util": "github:jspm/[email protected]",
      "websocket-extensions": "npm:[email protected]"
    },
    "npm:[email protected]": {
      "process": "github:jspm/[email protected]"
    }
  }
});

Part of fixing this issue should be adjusting the documentation to tell people to do whatever they need to to fix this (if it is in fact a configuration problem).

There is no method to unregister DI rule.

Hi, could you please add Container.unregister method? Because i don't see any way to unregister existing dependecy from Container, as i understand i can override it with null value, but it looks like a hack. What do you think about it?

Cointainer get new instance

I have realized that Container.get method returns same instance whenever it's called. I have tried to put this test case and it fails:

    class Logger {}

    class App {
      static inject() { return [Logger]; };
      constructor(logger) {
        this.logger = logger;
      }
    }

    var container = new Container();
    var firstApp = container.get(App);
    var secondApp = container.get(App);

    expect(firstApp).not.toBe(secondApp);

Do I need to create new container instance every time when I want new instance of App class?

import from aurelia-dependency-injection behaves like export default

I've tried to import di into sample (jspm) webapp, and imported variable from it is default exported module in which we have library clasess.

I have checked out index.js and there is no default export, so is it some feature of 6to5? Maybe this is desired behavior, but at first glance, it looks different than imports in unit tests.

import di from 'aurelia-dependency-injection';
let container = new di.Container();

VS

import {Container} from 'aurelia-dependency-injection';
let container = new Container();

Can't get by name using decorator

This works:

        class AppX{
          foo() { console.log('foo')}
        };
        var container = new Container();
        container.registerTransient('AppX', AppX);
        var app = container.get('AppX');
        expect(app).toEqual(jasmine.any(AppX));

but this does not:

        @transient('AppX')
        class AppX{
          foo() { console.log('foo')}
        };
        var container = new Container();
        var app = container.get('AppX');
        expect(app).toEqual(jasmine.any(AppX));

I understand the reason is being the container does not have a handle to AppX when calling container.get('AppX') so autoRegister wouldn't able to find the function and get the meta data attached to it.

I want this feature for generic rendering, i.e. the engineer would not know the type in advance.
This is the best approach because each component (AppX) can decorate its intent usage.

If I have to rely on container.registerTransient(...), I need to find a place to do the registration where the container is known, and it becomes the responsibility of the consuming engineer (typically application engineer) to do the registration, not ideal.

would it work well if there is a Conatiner.decorationContainer that will have a hold of the references and the resolving logic will consult it at container.get()?

I would be happy to contribute.

Automatic injection of `App` does not work with the initial route module

Here's the App and its router:

export class App {
  session:AppSession = new AppSession();

  private router: RouteConfig;

  configureRouter(config:RouterConfiguration, router: RouteConfig) {
    config.title = 'one';
    config.options.pushState = true;
    config.addPipelineStep('authorize', AuthorizeStep);
    config.map([
      { route: ['', 'one'], name: 'one', moduleId: 'one', auth: true, title: 'one' },
    // ...
    ]);

    this.router = router;
  }
}

Here's One:

@inject(App)
export class One {
  // this does not work (error "trying to inject something that doesn't exist):
  constructor(app:App) {
    this.app = app;
  }
}

However if we replace it with this, it does work:

import {Container} from "aurelia-dependency-injection";
export class One {
  // manually getting the object from the DI:
  constructor() {
    this.app = Container.instance.get(App);
  }
}

I suppose the App instance is added to the DI store too late. I even tried Lazy.of(App) but for some reason that gives the same error, even after setTimeout of a second (why?).
I should add that if the initial route's class doesn't try to inject App, but the user navigates to one that does, the injection does work (so the problem should be somewhere between the router and DI).

Injection in case of class extention

I am not sure if this is an issue at all but I would like to ask at least a question.
I have a case of extending a class like this:

@autoinject
export class Child {
    constructor(instanceOfSomeClass: SomeClass) { }
}

export class Parent extends Child {

}

In this case if I try to inject somewhere class Parent I am reciving error 'key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?'. What I had to do is to add autoinject attribute to Parent class and repeat (or extend) it's constructor and call Child constructor. I mean something like this:

@autoinject
export class Parent extends Child {
    constructor(instanceOfSomeClass: SomeClass) {
        super(instanceOfSomeClass);
    }
}

In earliar versions (before beta) I didn't have to do this. Is this intentional or...?

autoinject does not always work

I like the concept of autoinject, and have implemented it in my app replacing inject.

And it seems to work great, most of the time. Inject works 100% of the time. So far is is unreproducible, however. But I have attempted to get this working several times. Each time, it starts out working as it should, and then at some point will stop working again. If I switch the offending code back to inject, it works until the next viewmodel uses autoinject and it will fail there. There is no error message other than the expected injections are null.

If I can nail this down better, I will but I did want to mention it in case anyone else is running into the same issue.

Singleton Metadata Fix

The Singleton Registration metadata should register in the root container by default, and allow the option to register in the child. The default should be the root though.

autoinject not working

Hi

I'm working in TypeScript and using gulp/typescript plugin to traspile TS to JS ES5. Since I upgraded to latest aurelia version and run npm install and manually run jspm install for all aurelia plugins autoinject attribute stopped workig.

Thanks

jspm minified bundle throws an error with di included

I have some strange behavior when I want to compile some project with di into minified bundle. Not-minfied bundle is doesn;t throw any error. I have posted this issue at jspm-cli here but it could be di related issue.

Also , you can see this console error in my sample project which I have exposed to gh pages:
http://djindjic.github.io/jspm-bundle/

app is pretty simple:
https://github.com/djindjic/jspm-bundle/blob/master/lib/app.js

and If I comment static annotations() there is no error

ie9 support?

just want to ask do you plant to supprort ie9 for di?

Bug with @autoinject and TypeScript 1.5

Hi,

I have the following TypeScript 1.5 code:

import {autoinject,inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";
import * as JSData from "js-data";

const baseUrl = "/api/auth";

@autoinject // BUG: store in constructor is anonymous function
export class AuthManager {

    constructor(client : HttpClient, store:JSData.DS) {
        this.http = client;
        this.store = store; //injected store is an anonymous function that returns undefined

        this.meDao = this.store.defineResource<Domain.Me>("me");
    }

The TS ES5 output is as follows:

//NOTICE: Missing "js-data" define import.
define(["require", "exports", "aurelia-framework", "aurelia-http-client"], function (require, exports, aurelia_framework_1, aurelia_http_client_1) {
    var baseUrl = "/api/auth";
    var AuthManager = (function () {
        function AuthManager(client, store) {
            this.http = client;
            this.store = store;
            this.meDao = this.store.defineResource("me");
        }

First problem here is "js-data" is actually _missing_ from the define() call. @louislewis2 helped me find a hack-ish work around that forces an import by adding /// <amd-dependency path="js-data" /> to the top of my .ts file:

/// <amd-dependency path="js-data" />
import {autoinject,inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";

const baseUrl = "/api/auth";

import * as JSData from "js-data";
@autoinject // BUG: store in constructor is anonymous function
export class AuthManager {

    constructor(client : HttpClient, store:JSData.DS) {
        this.http = client;
        this.store = store; //injected store is an anonymous function that returns undefined

        this.meDao = this.store.defineResource<Domain.Me>("me");
    }

And finally "js-data" is imported; however, the injected store is _still_ an anonymous function that returns undefined.
screen444

Ultimately, going down this road with @autoinject won't work.


However, if I use @inject(HttpClient, JSData.DS), everything will work as expected (including removing /// <amd-dependency path="js-data" /> hacky workaround).

import {autoinject,inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";
import * as JSData from "js-data";

const baseUrl = "/api/auth";

@inject(HttpClient, JSData.DS) //Works correctly.
export class AuthManager {

    constructor(client : HttpClient, store:JSData.DS) {
        this.http = client;
        this.store = store; //works exactly as expected

        this.meDao = this.store.defineResource<Domain.Me>("me");
    }

screen445

I filed this bug report because I think the expected behavior between using @autoinject and @inject(...) should be the same (I hope).

Thanks,
Brian

PS. I've configured TS to emitDecoratorMetadata and the HttpClient is injected in both cases.

Usage without Aurelia?

I have to say I really like this functionality that Aurelia provides (well a lot of stuff aurelia does), but I was wondering if it is possible to use this library to inject in any normal ES6 classes or is this somehow coupled to Aurelia?

Container.get() looses type safety

Hi,
in TypeScript definitions there is class Container, get method definition as:
get(key: any): any;

It should be like this:

    get<T>(key: {new(...args): T}): T;
    get(key: any): any;

then we can write more fluently and have typing:

container.get(SomeRepository).findById(123);
instead of: var repo = <SomeRepository>container.get(SomeRepository);

I don't know how this container is exactly used, but maybe there is no need for key:any, return any at all.

You can find more info about this kind of type definitions at:

https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Generics.md

Dependency injection depends on path to import.

I have two view models and one service that is dependency injected into each of them.

ModelA

import { inject } from 'aurelia-dependency-injection';
import { Foo } from 'foo';
@inject(Foo)
export class ModelA {
    constructor(foo) {
        this.foo = foo;
    }
}

ModelB

import { inject } from 'aurelia-dependency-injection';
import { Foo } from 'services/foo';
@inject(Foo)
export class ModelB {
    constructor(foo) {
        this.foo = foo;
    }
}

Foo

export class Foo {
}

In this scenario, ModelA and ModelB will get different instances of Foo. This is because they import from different paths.

_IMPORTANT_: Both paths resolve to the same location at runtime! In config.js (for SystemJS) there are the following mappings:

"paths": {
    "*": "*.js",
}
"map": {
    "Foo": "services/Foo.js"
}

The expectation here would be that both models would get the same instance since they are reading the same class file off disk.

Breaks under minification

The following (from util.js) breaks if minified:

if (!(function f() {}).name)

When minified, the f get's removed (meaning the name is empty, thus the test succeeds, and it progresses to run Object.defineProperty(Function.prototype, "name" which crashes due to it already existing.

throw when key is undefined or null

should registerInstance et al. throw when the key argument is undefined or null?

scenario:

import {Foo} from 'aurelia-binding';  // aurelia-binding doesn't export "Foo"

aurelia.withInstance(Foo, new FancyFoo());  // no error

Noticed this because of my bug here: aurelia/binding#21

Let me know- will put together a PR.

ES6++ Traceur options

How can we use AtScript annotations in test? I see that karma-6to5-preprocessor is used instead of traceur.

Circular dependencies

For the ORM I'm building, I'm trying to create a decorator which lets you specify which Entity a particular property belongs to (association aka relation). The decorator is called association.

Example:

import {association, Entity} from 'spoonx/aurelia-orm';
import LocationEntity from './location';

export default class Company extends Entity {
  constructor () {
    super(...arguments);

    this.setResource('company').enableValidation();
  }

  @association(LocationEntity)
  locations = null;
}

Now, when fetching a Company and populating the result (into entities that do stuff) you get a Company instance, with a locations property which is a Location entity (it uses the container in the library to fetch instances). This works fine.

The problem I'm seeing, is when I try to do specify an association from both sides. The reason for this is that now, you have a circular dependency. Location wants to specify Company as an association, and Company wants to specify Location as a dependency.

It's not really a bug, but more of a problem I'm facing because of the way DI works. If DI were to be string based (e.g. define dependencies by key, and use that key to fetch dependencies) I would be able to lazy get the dependencies from the container.

Any help, tips or suggestions would be greatly appreciated.

Inject strings

My use case is PouchDB, but I assume other libraries may have this same problem. PouchDB doesn't let me extend it (pouchdb/pouchdb#3743). Unfortunately this means it's very hard to both use aurelia's DI and provide it with the dbName. If 'extends' worked I could simply do

export class Db extends PouchDB {
  constructor() {
    super('dbName')
  }
}

Seems like I should be able to inject a string directly. But this throws a typeError

@inject('dbName')
export PouchDB

For now the best option seems to be exporting a single string to solve this issue? I am okay with this but wanted to make sure...

// ./constants.js
export let dbName = 'dbName'

// ./db.js
import {dbName} from './constants'

@inject(dbName)
export PouchDB

Splat import breaks @autoinject

I'm not sure why, but when I use a splat import the @autoinject feature breaks.

import {autoinject} from 'aurelia-framework';
...
import * as core from '../core/module';
import * as utils from '../utils/module';
import {AppRoutes} from './module';
...
@autoinject
export class Shell {
...
    constructor(log: utils.Log, settings: core.Settings, routerConfig: AppRoutes) {
        this.logger = log;
        this.settings = settings;
        this.routerConfig = routerConfig;

        //When debugging in chrome, this variable gets succesfully created.
        //but, the settings injection is not working via @autoinject
        let blah = new core.Settings();
    }
...
}

For some reason, the constructor gets injected with two empty objects for log and settings. When I new out a new Settings(), I get the correct type as expected.

I created a tag so you can checkout the project/tag and gulp watch in order to debug and get the stack trace.

https://github.com/Roustalski/au-demos/blob/broke-auto-inject/modular-ts/src/client/app/layout/shell.ts#L68

Question about child containers

Hey All,

I'm trying to use a child containers to isolate dependencies used during testing...

I want to define a Storage class and in the production application leverage a PersistentStorage instance of Storage, while in the tests I want my objects to use an instance of InMemoryStorage instead (so the tests don't pollute the persistent storage).

It seems like when I try to pull an object out of the childContainer that isn't explicitly registered there, it's going up to the parent container, grabbing the instance, but the dependencies of the object being pulled out of the parent container use the parent container objects and not the overriden ones in child container.

Here's a test that describes what I'm hoping to accomplish.


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

describe.only("hmmm", function(){
    it("what do you think about this?", function(){

        class Storage{
            constructor(name) {
                this.name = name;
            }
        };
        class PersistentStorage extends Storage{
            constructor() {
                super("persistent");
            }
        };
        class InMemoryStorage extends Storage{
            constructor() {
                super("inmemory");
            }
        };

        class MyObject{
            static inject(){return [Storage];}
            constructor(storage) {
                this.storage = storage;
            }
        }

        var container = new Container();
        container.registerInstance(Storage, new PersistentStorage());

        var unitTestChildContainer = container.createChild();
        unitTestChildContainer.registerInstance(Storage, new InMemoryStorage());

        var myObj = unitTestChildContainer.get(MyObject);

        expect(myObj.storage.name).to.equal('inmemory');
    });
});

Could this be possible? Or is there a different approach to this without re-registering each object?

Thanks

Injecting a third party javascript with parameters in constructor

I am trying to get Aurelia working with this Azure AD ,but i am unable to get it working.

I modified the adal.js file a bit to be able to load it with systemjs:

define(['exports'], function (exports) {
    'use strict';

    exports.__esModule = true;
    exports.AuthenticationContext = function (config) { return new AuthenticationContext(config); }
     ...
     var AuthenticationContext;
     ...
     AuthenticationContext = function (config) {
     ...

and i try to inject like this:

import {inject} from "aurelia-framework";
import {AuthenticationContext} from "adal"

@inject(AuthenticationContext)
export class AdalService {    

    private authContext: AuthenticationContext;

    public adalConfig = {
        instance: 'https://login.microsoftonline.com/',
        tenant: 'xxxxx.onmicrosoft.com',
        clientId: 'xxxxxx',
        extraQueryParameter: 'nux=1',
        cacheLocation: 'localStorage',
        redirectUri: window.location.host
   }

    constructor(authenticationContext) {
        this.authContext = new authenticationContext(this.adalConfig);
    }
    ...

And every seems to work except that the config is never passed to the javascript. e.g. in the adal.js the parameter 'config' is always 'undefined'.

How do i get this working without modifying the third party javascript to much.

Docs

Hello,

I'm interested to try aurelie di, do you have some documentation for "users" to start with?

Can't set a single instance of EventAggregator shared by services and view models

It's me and my singletons again! I have this in my config.js

a.use.container.registerSingleton(EventAggregator); 

In my root component I get the bus instance

this.bus = Container.instance.get(EventAggregator);
console.log(this.bus);

In another service I'm trying to access the same instance of the bus

export class CachedAssets {
    constructor() {
        console.log("init cached assets");
        console.log(Container.instance.get(EventAggregator));
    }
}

The result is 2 different instances. Either Container.instance is not the global root (I've tried with makeGlobal() too, no difference) or the services root container is different than the components root container. The bottom line is I can't have a singleton for the EventAggregator that can be shared by all services/view models. Or I don't know how to set it up to work like that.

Object's lifetime clarifications

May be I'm missing something, but it states in docs:
By default, the DI container assumes that everything is a singleton instance; one instance for the app

And it is exactly the opposite - any object injected by @inject is transient (constructor invoked each time). Now, I can change it and place @singleton(false) and the constructor will be invoked only once, however isn't it misleading (singleton=false means transient)?

Did I misunderstand something?

Expecting `Parent` resolver to create new instance in parent container instead of the root

Let's imagine a situation when we have three (or more) levels of the Container:

class Lift {
}

@inject(Parent.of(Lift))
class Flat {
  constructor(public lift:Lift) {
  }
}

const streetContainer = new Container();

const house1Container = streetContainer.createChild();
const house2Container = streetContainer.createChild();

const flat11Container = house1Container.createChild();
const flat21Container = house2Container.createChild();

const flat11 = flat11Container.get(Flat);
const flat21 = flat21Container.get(Flat);

expect(flat11.lift).to.not.equal(flat21.lift);

I don't want to share lift between two different houses on a street ๐Ÿ˜„. I expected that Parent resolver will register a new instance of the class exactly in the parent context, not in the root. Actual behavior is done by the recursion in get() method which always searches the upper context, when there is no appropriate instance in the current.

My proposal is to change behavior of the Parent resolver to look for the appropriate instance only one level up. If you need also the current behavior, maybe there should be a new Ancestor resolver which will search all upper levels of the container.

Singleton change / issues

Two releases ago, I had ViewModel caching / reuse (to preserve user state accross navigation) working by putting @singleton(false) on top of my ViewModels.

Since updating to current Aurelia release, it doesn't work anymore.

Did something change or break in the API? How should I currently tell Aurelia to re-use my ViewModels?

Hooking the debugger shows that although there is the @singleton(false) decoration, my ctor gets called everytime (-> new instance).

Enhanced Dependency Injection Use

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?

add support for angular2 style constructor parameter decorators

in angular 2 di you can decorate each constructor parameter individually. This is very desirable for reasons such as proximity principle, ability to export classes inline (otherwise syntax error if decorator on class), more future proof (what if constructor overloading supported someday?). It would be awesome to see this feature added to aurelia dependency-injection.

snip from dependency-injection-in-angular-2:

import { Inject } from 'angular2/di';

class Car {
  constructor(
    @Inject(Engine) engine,
    @Inject(Tires) tires,
    @Inject(Doors) doors
  ) {
    ...
  }
}

decorators on class property declarations throwing error

I thought that I should be able to add a decorator to a property declaration within a class (used as a controller) and all would be fine, and it is within babel outside of aurelia. However, the following code causes an error within aurelia.

const aDecorator = (t, k, d) => {
    return d;
}

export class Thing {

 @aDecorator
  aProp;

}

If I don't modify the descriptor I get

TypeError: Cannot assign to read only property aProp

If I add a setter method i.e.

const aDecorator = (t, k, d) => {
  let x;
  d.set = y => x = y;
  d.get = () => x;
  return d;
}

I get

Error: Error invoking Thing.

However, if I just set the descriptors writable property to be true and nothing else aurelia no longer throws an error.

Am I doing something wrong/missed something? Are decorators not allowed within aurelia unless they are sanctioned by the framework itself?

Thanks

Child container is injecting root container into resolved instances

Hi,

Here a minimal example of what I mean (in TypeScript..)

let root = new Container();
let child = root.createChild();

@autoinject
class Test {
  constructor(container: Container) {
    console.log(container === root);  // true
    console.log(container === child); // false
  }
}

child.get(Test);

In that scenario, I would expect the child container to inject itself into the resolved instance and not the root.

Cheers,
Andreas

get access to aurelia container in decorators

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?

How to inject class type (not instance) with dependencies already resolved

In Angular 1.x, we can do this:

module.factory('MyClass', function (dep) {
  return function MyClass(arg) {
    this.arg = dep(arg);
  };
});

module.controller('MyCtrl', function (MyClass) {
  var instance = new MyClass(1234);
});

How can I achieve the same thing in Aurelia?

Currently I'm achieving it like this:

class MyClass {
  constructor(dep, arg) {
    this.arg = dep(arg);
  }
}

@inject(dep)
class MyViewModel {
  constructor(dep) {
    this.myClass = new MyClass(dep, 1234);
  }
}

but this means I need to inject dep into the view model so that I can pass an instance of it to the MyClass constructor. Is this the correct/only way of doing it?

container.getAll returns inconsistent type

The questionable behavior I'm seeing:

  1. If the container contains no values for a given key, container.getAll(key) returns an empty array.
  2. If the container contains only one value for a given key, container.getAll(key) returns the value.
  3. If the container contains more than one value for a given key, container.getAll(key) returns an array of values.

Expected behavior is that container.getAll(key) always returns the same type (i.e, an array, containing either zero, one, or multiple values). Certainly, this is what the jsdoc and typescript annotations for container.getAll suggest:

    /**
      * Resolves all instance registered under the provided key.
      * @param key The key that identifies the objects to resolve.
      * @return Returns an array of the resolved instances.
      */
    getAll(key: any): any[];

The issue also affects the All.of(key) resolver (which uses the container.getAll API).

Uncaught Error: key cannot be null or undefined.

If I mistakenly import a object from a location where it does not exist like

import {EventAggregator} from 'aurelia-framework';

system.js imports EventAggregator as undefined, then when I inject the EventAggregator into my element it gives the error below

Uncaught Error: key cannot be null or undefined.
    at Container.get [as superGet] (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/container.js:122:19)
    at Container.eval (eval at evaluate (unknown source), <anonymous>:1:6)
    at Object.InjectedScript._evaluateOn (<anonymous>:895:55)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:828:34)
    at Object.InjectedScript.evaluateOnCallFrame (<anonymous>:954:21)
    at Container.elementContainerGet [as get] (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/view-factory.js:43:16)
    at Container.invoke (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/container.js:200:30)
    at Array.<anonymous> (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/container.js:79:48)
    at Container.get [as superGet] (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/container.js:136:28)
    at Container.elementContainerGet [as get] (http://localhost:9000/jspm_packages/github/aurelia/[email protected]/view-factory.js:43:17)

According to @PWKad "I believe there is an open issue for that but if I remember correctly the problem is that it is trying to find a container by key but the key passed in was null. You could open an issue on DI to remember for later to improve that error message."

If possible please improve error message.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.