Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fluentProvide - example #143

Open
wiulma opened this issue Jun 20, 2018 · 10 comments
Open

fluentProvide - example #143

wiulma opened this issue Jun 20, 2018 · 10 comments

Comments

@wiulma
Copy link

wiulma commented Jun 20, 2018

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

@jeremy-coleman
Copy link

same issue here

@kferrone
Copy link

kferrone commented Sep 6, 2018

Was about to open same issue. +1million

@rmvermeulen
Copy link

rmvermeulen commented Feb 17, 2019

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.

@wiulma
Copy link
Author

wiulma commented Mar 2, 2019

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!

@rmvermeulen
Copy link

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();

@wiulma
Copy link
Author

wiulma commented Mar 14, 2019

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

@rmvermeulen
Copy link

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.

@JimmyBjorklund
Copy link

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 };

@tandt53
Copy link

tandt53 commented Sep 28, 2022

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)

@SennaSanzo
Copy link

SennaSanzo commented Dec 1, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants