typestack/typedi

Must there provide a name for @Service and @Inject?

Diluka opened this issue ยท 12 comments

I use empty @Service() and @Inject(), so raise an error TypeError: Cannot read property 'bind' of undefined

I add a name to both then they work fine.

typedi 0.4.2
node 6.2.1

can you provide a code sample?

todo.controller.ts

import {Get, Post, UseInterceptor, Body, Controller} from "routing-controllers";
import {TodoService} from "../service/todo.service";
import {Inject} from "typedi";
import * as _ from "lodash";
import * as AV from "leanengine";

@Controller("/todos")
export class TodoController {

    @Inject() private todoService:TodoService;


    @Get("/")
    @UseInterceptor((req, res, content) => {
        if (_.isArray(content)) {
            return _.map(content, (o:AV.Object) => o.toJSON());
        }
        return content;
    })
    getAll() {
        return this.todoService.getAll();
    }

    // @Post("/")
    // post(@Body() data) {
    //     return this.todoService.save(data);
    // }
}

todo.service.ts

import * as AV from "leanengine";
import {Service} from "typedi";
import {Todo} from "../entity";


@Service()
export class TodoService {
    getAll() {
        return new AV.Query(Todo).find<AV.Object[]>();
    }

    save(data) {
        return new Todo(data).save<AV.Object>();
    }
}

app.ts

import "reflect-metadata";
import {createExpressServer, useContainer} from "routing-controllers";
import {Container} from "typedi";
import * as Express from "express";
import * as AV from "leanengine";

// load all services
import "./service";

useContainer(Container);

export const app: Express.Application = createExpressServer({
    controllerDirs: [__dirname + "/controller/*.controller.js"]
});

app.use(AV.express());

error stack

TypeError: Cannot read property 'bind' of undefined                                                                                                                                                
    at Function.Container.get (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\typedi\0.4.2\typedi\Container.js:64:39)                                                    
    at Object.Container_1.Container.registerPropertyHandler.getValue (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\typedi\0.4.2\typedi\decorators.js:31:70)            
    at D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\typedi\0.4.2\typedi\Container.js:110:40                                                                            
    at Array.forEach (native)                                                                                                                                                                      
    at Function.Container.applyPropertyHandlers (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\typedi\0.4.2\typedi\Container.js:105:14)                                 
    at Function.Container.get (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\typedi\0.4.2\typedi\Container.js:66:14)                                                    
    at Object.getFromContainer (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\routing-controllers\0.6.2\routing-controllers\container.js:36:42)                         
    at ControllerMetadata.Object.defineProperty.get [as instance] (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\routing-controllers\0.6.2\routing-controllers\metadata\
ControllerMetadata.js:27:32)                                                                                                                                                                       
    at ActionMetadata.executeAction (D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\routing-controllers\0.6.2\routing-controllers\metadata\ActionMetadata.js:258:39)     
    at D:\Users\Diluka\Documents\GitHub\leancloud-demo\node_modules\.npminstall\routing-controllers\0.6.2\routing-controllers\RoutingControllerExecutor.js:74:33                                      

@Diluka how to reproduce this error in your demo? Im running node ./dist/app.js and have no issue (p.s. i added ("emitDecoratorMetadata": true and "experimentalDecorators": true) to your tsconfig.json)

thanks a lot

the same with typestack/routing-controllers#16

closing

@pleerock Sorry for commenting on a closed issue, but I'm also having this exact issue - example repo here: https://github.com/mogusbi/typedi-error

Whenever I hit my routing-controller endpoint, I'm met with this error

{
    "name": "TypeError",
    "message": "Cannot read property 'readAll' of undefined",
    "stack": "TypeError: Cannot read property 'readAll' of undefined\n    at QuestionController.httpGetAll (/Volumes/Data/Users/mo/Desktop/typeorm/src/controllers/question/question.controller.ts:11:32)\n    at ActionMetadata.callMethod (/Volumes/Data/Users/mo/Desktop/typeorm/src/metadata/ActionMetadata.ts:254:62)\n    at /Volumes/Data/Users/mo/Desktop/typeorm/src/RoutingControllers.ts:123:142"
}

Here's a quick recap of my code

// question.controller.ts

import {Body, Delete, Get, HttpCode, JsonController, Param, Post, Put} from 'routing-controllers';
import {Inject} from 'typedi';
import {IQuestion, Question, QuestionService} from '../../entities/question';

@JsonController('/question')
export class QuestionController {
  @Inject() private questionService: QuestionService;

  @Get()
  public httpGetAll (): Promise<[Question[], number]> {
    return this.questionService.readAll();
  }

  @Get('/:id')
  public httpGetOne (
    @Param('id') id: string
  ): Promise<Question> {
    return this.questionService.readOne(id);
  }

  @Post()
  @HttpCode(201)
  public httpPost (
    @Body({
      required: true
    }) props: IQuestion
  ): Promise<Question> {
    return this.questionService.create(props);
  }

  @Put('/:id')
  public httpPut (
    @Param('id') id: string,
    @Body({
      required: true
    }) props: IQuestion
  ): Promise<void> {
    return this.questionService.update(id, props);
  }

  @Delete('/:id')
  public httpDelete (
    @Param('id') id: string
  ): Promise<void> {
    return this.questionService.drop(id);
  }
}
// question.service.ts

import {Service} from 'typedi';
import {Repository} from 'typeorm';
import {OrmRepository} from 'typeorm-typedi-extensions';
import {IQuestion} from './question.interface';
import {Question} from './question.model';

@Service()
export class QuestionService {
  @OrmRepository(Question) private repository: Repository<Question>;

  public create (props: IQuestion): Promise<Question> {
    return this.repository.persist(props);
  }

  public readAll (size: number = 10, page: number = 1): Promise<[Question[], number]> {
    const skip: number = size * (page - 1);

    return this.repository.findAndCount({
      skip,
      take: page
    });
  }

  public readOne (id: string): Promise<Question> {
    return this.repository.findOneById(id);
  }

  public update (id: string, props: IQuestion): Promise<void> {
    return this.repository.updateById(id, props);
  }

  public drop (id: string): Promise<void> {
    return this.repository.removeById(id);
  }
}
// www.ts

import * as http from 'http';
import 'reflect-metadata';
import {Container} from 'typedi';
import {createConnection, useContainer} from 'typeorm';
import {config} from '../config';
import {BaseEntity, Question} from '../entities';
import {app} from './app';

useContainer(Container);

createConnection({
  ...config.sql,
  entities: [
    BaseEntity,
    Question
  ]
}).then(
  () => http
    .createServer(app)
    .listen(8082, () => console.log('Server started on port 8082'))
).catch(
  (err: Error) => console.error(err.message)
);
// tsconfig.json

{
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "lib": [
      "es2015",
      "es2016"
    ],
    "module": "commonjs",
    "moduleResolution": "node",
    "noEmitHelpers": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "removeComments": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es5"
  },
  "awesomeTypescriptLoaderOptions": {
    "useWebpackText": true
  }
}

I am running typescript 2.4.1 and node 6.10.2

@mogusbi Hey, found out by looking at the @Diluka bug report code.

You certainly forgot :

import { Container } from "typedi";
import { useContainer } from 'routing-controllers';

useContainer(Container);

To place into your app.ts. It tells to routing-controllers to use the container from typeDI.

See this file.

@pleerock maybe it should appear somewhere in a "Troubleshooting" section or whatever ? I know this is related to routing-controller or whatever lib we use in complement of TypeDI, but I think a great number of developers may face this at some point. Idk, what you think ?

@gjdass perfect, that's working now. Thank you very much!

yeah we can add links in Troubleshooting section, like this:

Troubleshooting

  • how to use TypeDI with TypeORM
  • how to use TypeDI with routing-controllers
    ...

with links to appropriate qa. PRs are welcomed

@pleerock done ;) #32

Thank you!

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.