unloco
unloco

Reputation: 7320

Inversify circular singleton injection

I'm trying to use two singletons and make them able to call each other like this

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

let container = new Container();

@injectable()
class Dom {
  private domUi: DomUi;

  constructor (domUi: DomUi) {
    this.domUi = domUi;
  }
}

@injectable()
class DomUi {
  private dom: Dom;

  constructor (dom: Dom) {
    this.dom = dom;
  }
}

@injectable()
class Test {
  constructor (dom: Dom) {
    console.log(dom);
  }
}

container.bind<Dom>(Dom).toSelf().inSingletonScope();
container.bind<DomUi>(DomUi).toSelf().inSingletonScope();

const test = container.resolve(Test);

But it gives this error

Error: Missing required @inject or @multiInject annotation in: argument 0 in class Dom.

How can this be fixed? I tried the @inject and @multiInject with no luck!
is there a better way to think about this from a design pattern standpoint?

Upvotes: 2

Views: 7767

Answers (1)

Remo H. Jansen
Remo H. Jansen

Reputation: 24941

I think you have found a bug, you can use the following as a workaround:

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

let container = new Container();
let { lazyInject } = getDecorators(container);

@injectable()
class DomUi {
    private _dom: Dom;

    public constructor (dom: Dom) {
        this._dom = dom;
    }
}

@injectable()
class Dom {
    @lazyInject(DomUi) private domUi: DomUi;
}

@injectable()
    class Test {
    constructor(dom: Dom) {
        console.log(dom);
    }
}

container.bind<Dom>(Dom).toSelf().inSingletonScope();
container.bind<DomUi>(DomUi).toSelf().inSingletonScope();
const dom = container.resolve(Test);

The recommended implementation would be with symbols not classes as IDs:

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

let container = new Container();
let { lazyInject } = getDecorators(container);

const TYPE = {
    Dom: Symbol("Dom"),
    DomUi: Symbol("DomUi"),
    Test: Symbol("Test")
};

interface Dom {}
interface DomUi {}
interface Test {}

@injectable()
class DomUi {
    public dom: Dom;

    public constructor (
        @inject(TYPE.Dom) d: Dom
    ) {
        this.dom = d;
    }
}

@injectable()
class Dom {
    @lazyInject(TYPE.DomUi) public domUi: DomUi;
}

@injectable()
class Test {
    constructor(
        @inject(TYPE.Dom) d: Dom
    ) {
        console.log(d, d.domUi.dom);
    }
}

container.bind<Dom>(TYPE.Dom).to(Dom).inSingletonScope();
container.bind<DomUi>(TYPE.DomUi).to(DomUi).inSingletonScope();
container.bind<Test>(TYPE.Test).to(Test);
const dom = container.get(TYPE.Test);

Upvotes: 4

Related Questions