inversify-binding-decorators icon indicating copy to clipboard operation
inversify-binding-decorators copied to clipboard

fluentProvide - example

Open wiulma opened this issue 6 years ago • 10 comments

Could you please provide a full fluentProvide example? I have this kind of issue:

import { Container, decorate, inject, injectable, interfaces as inversifyInterfaces, named } from "inversify";
import { fluentProvide, buildProviderModule } from "inversify-binding-decorators";
import "reflect-metadata";

const container = new Container();
const provideFluent = fluentProvide(container);
....
export function ProvideAsSingleton(symbol: inversifyInterfaces.ServiceIdentifier<any>): any {
    return provideFluent(symbol).inSingletonScope().done(true);
}
container.load(buildProviderModule());

export { container };

The issue is at:

const provideFluent = fluentProvide(container);

the error is:

[ts]
Argument of type 'Container' is not assignable to parameter of type 'string | symbol | Newable<any> | Abstract<any>'.
  Type 'Container' is not assignable to type 'Abstract<any>'.
    Property 'prototype' is missing in type 'Container'.

How can I use fluent provider? thanks

wiulma avatar Jun 20 '18 12:06 wiulma

same issue here

jeremy-coleman avatar Jul 06 '18 17:07 jeremy-coleman

Was about to open same issue. +1million

kferrone avatar Sep 06 '18 18:09 kferrone

const provideFluent = fluentProvide(container); is broken because container is the container instance, which is not a valid service identifier (you'd for example use class Container for that). Your ProvideAsSingleton function should just use the library function and it will work.

The ~~buildProviderModule~~ container.load() line is the what couples your decorated classes to that specific container.

rmvermeulen avatar Feb 17 '19 08:02 rmvermeulen

Sorry, but still it seems not working. I can't inject classes in the container in any way. Perhaps I'm wrong in creating or exporting container, but it seems always empty.

Custom container: ` import { Container, inject, interfaces as inversifyInterfaces } from "inversify"; import { buildProviderModule, fluentProvide } from "inversify-binding-decorators"; import "reflect-metadata"; const container = new Container(); const provideFluent = fluentProvide(Container); container.load(buildProviderModule());

export function Provide(symbol: inversifyInterfaces.ServiceIdentifier): any { return fluentProvide(symbol).done(); }

export function ProvideAsSingleton(identifier: inversifyInterfaces.ServiceIdentifier): any { return fluentProvide(identifier) .inSingletonScope() .done(true); }

export function Inject(symbol: inversifyInterfaces.ServiceIdentifier): any { return inject(symbol); }

export { container }; `

Then I provide a class: @ProvideAsSingleton(Application) export class Application { ... }

and then I try to get it: container.get<Application>(Application).init() When I try to get the Application instance from container, the container has its _bindingDictionary map empty, and an error occurs: No matching bindings found for serviceIdentifier: Application

So...what I'm doing wrong? thanks!

wiulma avatar Mar 02 '19 19:03 wiulma

I think container.load() should be called when the decorators have already run (e.g. when importing from './App.ts'). The following code runs. Hope this helps

import { Container } from 'inversify';
import {
  buildProviderModule,
  fluentProvide,
} from 'inversify-binding-decorators';
import 'reflect-metadata';

type ArgTypes<Fn extends Function> = Fn extends (...args: infer A) => any
  ? A
  : never;

type Identifier = ArgTypes<typeof fluentProvide>[0];

export function ProvideAsSingleton(identifier: Identifier): any {
  return fluentProvide(identifier)
    .inSingletonScope()
    .done(true);
}

@ProvideAsSingleton(Application)
export class Application {
  init() {
    console.log('app init');
  }
}

const container = new Container();
container.load(buildProviderModule());

console.assert(container.isBound(Application), 'Application not registered');
const app = container.get(Application);
console.assert(app != null, 'Application invalid');
app.init();

rmvermeulen avatar Mar 04 '19 17:03 rmvermeulen

thanks for your reply, but I still have issue about this. In order to make it working, I have to import all classes that need to register to the container before call container.load(buildProviderModule()); and this is a different behaviuor compared with the old makeFluentProvideDecorator.

For example, if I want to register controllers or middleware in express in this way:

useExpressServer(exp, { controllers: [${mainPath}/controllers/{.js,.ts}], defaultErrorHandler: false, middlewares: [${mainPath}/middlewares/{.js,.ts}], routePrefix: ${serviceContext}/api});

I get this kind of error:

No matching bindings found for serviceIdentifier: HelloWorldController at _validateActiveBindingCount (...\node_modules\inversify\lib\planning\planner.js:62:23) at _getActiveBindings (...\node_modules\inversify\lib\planning\planner.js:48:5) at _createSubRequests (...node_modules\inversify\lib\planning\planner.js:85:26) at Object.plan (...\node_modules\inversify\lib\planning\planner.js:136:9) at ...\node_modules\inversify\lib\container\container.js:317:37 at Container._get (...\node_modules\inversify\lib\container\container.js:310:44) at Container.get (...\node_modules\inversify\lib\container\container.js:230:21) at Object.getFromContainer (....\node_modules\routing-controllers\container.js:37:42) at ControllerMetadata.get [as instance] (...\node_modules\routing-controllers\metadata\ControllerMetadata.js:24:32) at ActionMetadata.callMethod (....\node_modules\routing-controllers\metadata\ActionMetadata.js:107:58) So..how can I fix this scenario? thanks

wiulma avatar Mar 14 '19 20:03 wiulma

The files need to be required at some point before that. If the file with your class in it isn't evaluated, the class does not exist as far as javascript is concerned, and it isn't decorated so inversify has no clue either. I don't know how this worked with the old behavior; I wasn't using it then. If you group your controllers and middlewares in containers you only have to require those, the rest comes with it.

rmvermeulen avatar Mar 17 '19 12:03 rmvermeulen

This works for "inversify": "^5.1.1"

import "reflect-metadata";

import { autoProvide, fluentProvide, provide, buildProviderModule } from "inversify-binding-decorators";
import { Container, interfaces, inject } from "inversify";

const iocContainer = new Container();
const provideNamed = (identifier: string | symbol | interfaces.Newable<any> | interfaces.Abstract<any>, name: string) => {
  console.log(identifier);
  return fluentProvide(identifier).whenTargetNamed(name).done();
};

const provideSingleton = (identifier: string | symbol | interfaces.Newable<any> | interfaces.Abstract<any>) => {
  console.log(`Register`, identifier);
  return fluentProvide(identifier).inSingletonScope().done();
};

export { iocContainer, autoProvide, provide, provideSingleton, provideNamed, inject, buildProviderModule };

JimmyBjorklund avatar Feb 16 '22 07:02 JimmyBjorklund

I got same issue that container is empty even after loading provider module from inversify-binding-decorators. When putting all in 1 ts file, it works. Otherwise, it does not work.

  • type.ts
export let TYPE = {
    Weapon : "Weapon",
    Ninja: "Ninja"
}
  • weapon.ts
export interface Weapon {
    hit(): string;
}
  • katana.ts
import {fluentProvide} from "inversify-binding-decorators";
import {TYPE} from "./type";
import {Weapon} from "./weapon";
import "reflect-metadata"

@fluentProvide(TYPE.Weapon).whenTargetTagged("throwable", true).done()
export class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}
  • ioc.ts
import { Container} from "inversify";
import "reflect-metadata";
import {buildProviderModule} from "inversify-binding-decorators";
import {TYPE} from "./type";

const container = new Container();
container.load(buildProviderModule());
container.isBoundTagged(TYPE.Weapon, "throwable", true);

export {
    container
}
  • test.ts
import {Weapon} from "./sample/weapon";
import {container} from "./sample/ioc";
import {TYPE} from "./sample/type";

describe("Typescript usage suite", () => {
    it("should be able to execute a test", () => {
        const katana: Weapon = container.getTagged<Weapon>(TYPE.Weapon, "throwable", true);
        katana.hit();
    });
});
  • Error log:
1) Typescript usage suite
       should be able to execute a test:
     Error: No matching bindings found for serviceIdentifier: Weapon
 Weapon - tagged: { key:throwable, value: true }

      at _validateActiveBindingCount (node_modules/inversify/src/planning/planner.ts:113:15)
      at _getActiveBindings (node_modules/inversify/src/planning/planner.ts:91:3)
      at _createSubRequests (node_modules/inversify/src/planning/planner.ts:146:22)
      at plan (node_modules/inversify/src/planning/planner.ts:240:5)
      at /Users/tando/projects/mocha-examples/packages/typescript/node_modules/inversify/src/container/container.ts:623:25
      at Container._get (node_modules/inversify/src/container/container.ts:574:37)
      at Container._getButThrowIfAsync (node_modules/inversify/src/container/container.ts:580:25)
      at Container.getTagged (node_modules/inversify/src/container/container.ts:337:17)
      at Context.<anonymous> (src/index.spec.ts:7:42)
      at processImmediate (node:internal/timers:466:21)

tandt53 avatar Sep 28 '22 13:09 tandt53

I got same issue that container is empty even after loading provider module from inversify-binding-decorators. When putting all in 1 ts file, it works. Otherwise, it does not work.

  • type.ts
export let TYPE = {
    Weapon : "Weapon",
    Ninja: "Ninja"
}
  • weapon.ts
export interface Weapon {
    hit(): string;
}
  • katana.ts
import {fluentProvide} from "inversify-binding-decorators";
import {TYPE} from "./type";
import {Weapon} from "./weapon";
import "reflect-metadata"

@fluentProvide(TYPE.Weapon).whenTargetTagged("throwable", true).done()
export class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}
  • ioc.ts
import { Container} from "inversify";
import "reflect-metadata";
import {buildProviderModule} from "inversify-binding-decorators";
import {TYPE} from "./type";

const container = new Container();
container.load(buildProviderModule());
container.isBoundTagged(TYPE.Weapon, "throwable", true);

export {
    container
}
  • test.ts
import {Weapon} from "./sample/weapon";
import {container} from "./sample/ioc";
import {TYPE} from "./sample/type";

describe("Typescript usage suite", () => {
    it("should be able to execute a test", () => {
        const katana: Weapon = container.getTagged<Weapon>(TYPE.Weapon, "throwable", true);
        katana.hit();
    });
});
  • Error log:
1) Typescript usage suite
       should be able to execute a test:
     Error: No matching bindings found for serviceIdentifier: Weapon
 Weapon - tagged: { key:throwable, value: true }

      at _validateActiveBindingCount (node_modules/inversify/src/planning/planner.ts:113:15)
      at _getActiveBindings (node_modules/inversify/src/planning/planner.ts:91:3)
      at _createSubRequests (node_modules/inversify/src/planning/planner.ts:146:22)
      at plan (node_modules/inversify/src/planning/planner.ts:240:5)
      at /Users/tando/projects/mocha-examples/packages/typescript/node_modules/inversify/src/container/container.ts:623:25
      at Container._get (node_modules/inversify/src/container/container.ts:574:37)
      at Container._getButThrowIfAsync (node_modules/inversify/src/container/container.ts:580:25)
      at Container.getTagged (node_modules/inversify/src/container/container.ts:337:17)
      at Context.<anonymous> (src/index.spec.ts:7:42)
      at processImmediate (node:internal/timers:466:21)

Did you find a workaround for this ? Thanks.

Edit : I put this here, I don't know if it will help someone but the problem I had was just a problem of module imports. I start working again with node.js and javascript in general and I completely forgot about import order. So when you call buildProviderModule() be sure to import all the injectable entities beforehand. In my case I created a loader import file which is in charge for loading everything before the call.

You can find an exemple here : https://github.com/inversify/inversify-express-example/tree/master/BindingDecorators. Be careful to respect the import "path_to_file" syntax so that Typescript understands you want to force import the module without using it right now.

SennaSanzo avatar Dec 01 '23 15:12 SennaSanzo