inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.29k stars 716 forks source link

LazyServiceIdentifer does not work for property injection #944

Open MeirionHughes opened 6 years ago

MeirionHughes commented 6 years ago

Unable to lazily inject circular service dependency via property injection.

Side-note: Identifer -> Identifier

Expected Behavior

Inject without error

Current Behavior

throws error

Steps to Reproduce (for bugs)

import "reflect-metadata";
import { Container, injectable, inject, LazyServiceIdentifer } from "inversify";

const container = new Container();

interface IModuleA {}
interface IModuleB {}

const moduleA = Symbol.for("IModuleA");
const moduleB = Symbol.for("IModuleB");

@injectable()
class ModuleA implements IModuleA {
  @inject(new LazyServiceIdentifer(()=>moduleB))
  b: IModuleB;
}

@injectable()
class ModuleB implements IModuleB{
  @inject(new LazyServiceIdentifer(()=>moduleA))
  a: IModuleA
}

container.bind(moduleA).to(ModuleA).inSingletonScope();
container.bind(moduleB).to(ModuleB).inSingletonScope();

let bar = container.get(moduleA);

console.log(bar);

Context

I'm aware that injecting via the constructor works; however in NInject I was able to do this kind of lazy property injection within a base "Module" class. Subsequent derivations of that "Module" class then didn't have to deal with adding those dependencies manually.

Environment

inversify: "^4.13.0" node-js: 10.1.0

Stack trace

Error: No matching bindings found for serviceIdentifier: [object Object]
    at _validateActiveBindingCount (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:62:23)
    at _getActiveBindings (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:48:5)
    at _createSubRequests (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:91:26)
    at D:\Code\logger\node_modules\inversify\lib\planning\planner.js:115:17
    at Array.forEach (<anonymous>)
    at D:\Code\logger\node_modules\inversify\lib\planning\planner.js:114:26
    at Array.forEach (<anonymous>)
    at _createSubRequests (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:94:20)
    at Object.plan (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:136:9)
    at D:\Code\logger\node_modules\inversify\lib\container\container.js:318:37
Halynsky commented 6 years ago

I have tried to reproduce the same case (A injected in B. B injected in A.) with @lazyInject but also getting an error. And also I tried an example with the service identifier. https://github.com/inversify/InversifyJS/issues/865

bl-packages-publisher commented 4 years ago

Any progress on this issue?

mohsinamjad commented 3 years ago

Any updates ? facing same issue with lazyServiceIdentifier

VivienneB commented 3 years ago

I resolved this problem by defining a third class, class C, which has an A and B injected property. This will contain methods which should call each property's methods.

dustinlacewell commented 3 years ago

This really really hurts.

vadistic commented 2 years ago

Just had an issue with that. My workaround:

a) Inject Container to container somewhere

import { Container } from 'inversify'

const container = new container({...})

container.bind(Container).toConstantValue(container)

b) Implement custom lazy inject getter decorator based on container.get()

import { interfaces, decorate, Container, METADATA_KEY } from 'inversify'

export function lazyInject(
  serviceIdentifierCallback: () => interfaces.ServiceIdentifier<any>,
): PropertyDecorator {
  return function (target: any, propertyKey) {
    // inject container as this.container if it's not present
    if (!Reflect.getMetadata(METADATA_KEY.TAGGED_PROP, target.constructor)?.container) {
      decorate(inject(Container), target, 'container')
    }

   // define getter
    Object.defineProperty(target, propertyKey, {
      get: function () {
        return this.container.get(serviceIdentifierCallback())
      },
    })
  }
}

c) Usage

@injectable()
export class MyClass {
     @lazyInject(() => SOME_TOKEN) service!: SomeService
}

The caveats are:

cuidong626 commented 1 year ago

Any progress on this issue?

dustinlacewell commented 1 year ago

I don't think anyone is working on Inversify