Cannot Inject Dependency Into Derived Class - javascript

I have a relatively simple setup with three classes. I am using inversify for dependency injection. But when injecting the class MessageBroker into the derived class Repository the MessageBroker is undefined:
import 'reflect-metadata';
import { injectable, inject, Container, unmanaged } from 'inversify';
const container = new Container();
const registerProviders = (...providers: any[]) =>
providers.forEach(provider => container.bind(provider.name).to(provider));
const getProvider = (provider): any => container.get(provider.name);
#injectable()
export class MessageBroker {
start = () => console.log('init message broker');
}
#injectable()
export abstract class Repository {
#inject(MessageBroker.name) private mb: MessageBroker;
constructor(#unmanaged() protected readonly user: any) {}
// this.mb is undefined
initialize = () => this.mb.start();
}
#injectable()
export class UserRepository extends Repository {
constructor() {
super({ user: 'some object' });
this.initialize();
}
}
registerProviders(UserRepository, Repository, MessageBroker);
const repo: UserRepository = getProvider(UserRepository);
You can try it yourself. I've created a small GitHub repository: https://github.com/flolude/stackoverflow-inversify-injected-service-undefined
When running the script, I get this error:
/project/index.ts:22
initialize = () => this.mb.start();
^
TypeError: Cannot read property 'start' of undefined
at UserRepository.Repository.initialize (/project/index.ts:22:30)
at new UserRepository (/project/index.ts:29:10)
at _createInstance (/project/node_modules/inversify/lib/resolution/instantiation.js:21:12)
at Object.resolveInstance (/project/node_modules/inversify/lib/resolution/instantiation.js:41:18)
at /project/node_modules/inversify/lib/resolution/resolver.js:72:42
at Object.resolve (/project/node_modules/inversify/lib/resolution/resolver.js:96:12)
at /project/node_modules/inversify/lib/container/container.js:319:37
at Container._get (/project/node_modules/inversify/lib/container/container.js:310:44)
at Container.get (/project/node_modules/inversify/lib/container/container.js:230:21)
at getProvider (/project/index.ts:9:50)
P.S. I get pretty much the same error when compiling the code to Javascript

Your MessageBroker has only been set in memory but has never been instantiated, which is how it is getting the undefined error. In your constructor you will need to set
this.mb = new MessageBroker();
Another way you can do this without the above line is to add a empty parameter signatured constructor into the MessageBroker class.

Related

Webpack ES6 modules multiple class app organization

I'm building an app with webpack for the first time and i'm trying to wrap my head around organizing class files. I'm having trouble getting the code to work. I'm still new to ES6 and such, so my code below is probably very wrong but i'm not sure its my approach/concept or its my syntax.
entry is index.js and I also have these files
import App from './js/app.js';
import User from './js/user.js';
import Guest from './js/guest.js';
const app = new App();
const user = new User();
const guest = new Guest();
$(document).ready(function () {
app.DatabaseStore.senddata();
console.log( user.getall() );
});
src/js/app.js the main global method/variable class
import CookieStore from './cookie.js';
import DatabaseStore from './database.js';
export default class App {
constructor() {
this.cookieStore = new CookieStore();
this.databaseStore = new DatabaseStore();
}
gettime() {
return 'time';
}
}
src/js/user.js methods that are for users
import App from './app.js';
export default class User extends App {
constructor() {
this.mydata = App.cookieStore.getData();
console.log(this.mydata );
}
getall() {
return ['foo', 'bar', 'baz'];
}
}
src/js/guest.js methods that are for guests
import App from './app.js';
export default class Guest extends App {
constructor() {
this.mydata = App.cookieStore.getData();
}
}
src/js/cookie.js cookie manipulating methods
export default class CookieStore {
constructor() {}
mydata() {return 'foo';}
}
src/js/database.js firebase methods
export default class DatabaseStore {
constructor() {}
senddata() {
this.mydata = App.cookieStore.getData();
}
You are trying to access instance property statically. You need to create an instance of App class before trying to access cookieStore property. You can create an instance and export it in your app.js to have singleton instance.
//in your app.js
export const app = new App();
in other files
import {app} from './js/app.js'
app.cookieStore.getData();

How to automatically load files that contain specific decorators in a node project

I have created a decorator, in Project A (the main library) and would like to have all of those decorators automatically loaded when the app starts in Project B (the project using Project A). Is there anyway of doing this?
index.ts looks like this:
export function MyDecorator<T extends Controller>() {
return (target: new () => T) => {
// Do stuff with the decorator
}
}
const server = http.createServer((req, res) => {
})
server.listen(8080)
Is there something that I can do to automatically execute #MyDecorator() on all classes in Project B without Project B having to do so?
MyClass1.ts
import { MyDecorator } from 'project-a'
#MyDecorator()
export class ProjectBClass1 {}
MyClass2.ts
import { MyDecorator } from 'project-a'
#MyDecorator()
export class ProjectBClass2 {}
I assume you mean creating instances by load .
Also I'm not sure if that is an elegant solution but here is my suggestion:
Create a class that has a static method:
class ControllerCreator {
private static constrollerInstances: any = []
private static controllerConstructors : any = [];
static registerControllerClass(ctor: any) {
ControllerCreator.controllerConstructors.push(ctor);
}
static createInstances() {
ControllerCreator.controllerConstructors.forEach(
ctor => constrollerInstances.push(new ctor()) // pushing them to static array to not lose
)
}
}
In your decorator you should register your controller constructor:
export function MyDecorator<T extends Controller>() {
return (target: new () => T) => {
// Do stuff with the decorator
class newClass extends target {
// ...
}
ControllerCreator.registerControllerClass(newClass);
}
}
And finally at some point you should call:
ControllerCreator.createInstances();

Instantiate class dynamically but prototype is undefined

I tried to instantiate dynamically my class in a Vue JS with TypeScript project.
I tried this solution.
It works in Angular 2+ but in my Vue project I have this error :
Uncaught TypeError: Object prototype may only be an Object or null: undefined
I have a Classes.ts file
import {CMyClass} from 'path/to/my/class';
export const Classes = {
CMyClass: CMyClass
}
My class file :
import {CMyOtherClass} from 'path/to/my/mother/class';
export class CMyClass extends CMyOtherClass {
constructor() {}
}
My mother class file :
export class CMyOtherClass {
constructor() {}
}
And my function to instantiate a class :
import {Classes} from 'path/to/Classes/constante';
buildClass(className: string): any {
return new Classes[className]();
}
An idea ?

Property "somemethod" does not exist on type 'typeof "className"

I am getting a "Property 'searchStaff' does not exist on type 'typeof UserService'." error in my editor when I try to use this api service class. PS. I am new to typescript.
import axios from "axios";
import { Observable } from "rxjs";
class UserService {
public searchStaff(): Observable<any> {
return Observable.fromPromise(axios.get("./staffs/search"));
}
}
export default new UserService();
I am trying to get data from this class.
const result = UserService.searchStaff();
You can directly put export or export default in your declaration. So you can do:
export class UserService {
}
or
export default class UserService {
}
You can find the different methods of using modules for typescript here.
Also, looking at this line of code, const result = UserService.searchStaff(); are you instantiating UserService? If not, then you have to declare searchStaff as static.

Typescript: Inject generic & get ES6 module name

I am trying to build a generic repository using:
Typescript
ES6
Angular 1.x
But I can't figure out how I should inject the Entity and then get its module name.
The reason why i want to get the name:
Is because i follow a naming convention where a file called order-count.ts should render the URL '/order/count'
Is this solvable with Typescript/Javascript?
Here is what i have:
order-module.ts
import {App} from '../../App';
import {OrderService} from './order-service';
const module: ng.IModule = App.module('app.order', []);
module.service('orderService', OrderService);
order-service.ts
import {CrudService} from '../../shared/services/crud-service'
import {OrderCount} from '../order/entities/order-count';
export class OrderService {
// #ngInject
constructor(private crudService: CrudService<OrderCount>) {
this.crudService = crudService;
}
getOrders() {
var promise = this.crudService.getAll();
promise.then(response => {
console.log(response, 'success');
}, error => {
console.log(error, 'failed');
});
}
}
order-count.ts
import {Entity} from '../../../shared/models/entity';
export class OrderCount extends Entity {
storeId: string;
storeName: string;
}
entity.ts
export interface IEntity {
id: number;
}
entity.ts
import {IEntity} from '../../module/contracts/entities/entity';
export class Entity implements IEntity {
new() { }
id: number;
}
crud-service.ts
'use strict';
import { Entity } from '../models/entity';
import { EndpointService } from './endpointService';
export class CrudService<TEntity extends Entity> {
private baseCallPath: string;
private entity: { new (): Entity };
// #ngInject
constructor(private endpointService: EndpointService, private $http: ng.IHttpService) {
this.baseCallPath = new this.entity().constructor.name.replace('-', '/');
}
getAll(): ng.IHttpPromise<any> {
return this.handleResponse(
this.$http.get(this.endpointService.getUrl(this.baseCallPath)),
'getAll'
);
}
handleResponse(promise: ng.IHttpPromise<any>, callerMethodName: string): ng.IHttpPromise<any> {
return promise.success((data: any) => {
Array.prototype.push.apply(this.baseCallPath, data);
}).error((reason: any) => {
console.log(this.baseCallPath + callerMethodName, 'ERROR', reason);
});
}
}
endpoint-service.ts
export class EndpointService {
private baseUri: string = 'http://localhost:3000/api/';
getUrl(moduleName: string): string {
return this.baseUri + moduleName;
}
}
This link may be helpful in order to implement a generic repository with Typescript
Regarding the usage of class name as a value you may check this relevant question.
The good thing it can be retrieved and used as Foo.name or this.constructor.name. The bad thing is that it isn't available in every browser and should be polyfilled. Another bad thing is that minified function won't save its original name.
Wouldn't it be great to annotate function with Foo.name = 'Foo' on its definition and stick to pre-made property? Not really. Function.name is originally non-configurable, so it is read-only in a plethora of browsers.
If you don't plan to avoid minimization at all, or you're not too fond of configuring minifier to preserve class names (a solution faulty by design), don't use Function.name for anything like that.
The typical case for extendable ES6/TS class in Angular is
export class Foo {
static _name = 'Foo';
}
export default angular.module('app.foo', [])
.factory('Foo', Foo)
// if DRY is a must,
// .factory(Foo._name, Foo)
.name;
import { Foo } from './foo';
export class Bar extends Foo {
static _name = 'Bar';
}
export default angular.module('app.bar', []).factory('Bar', Bar).name;
import moduleFoo from './foo';
import moduleBar from './bar';
angular.module('app', [moduleFoo, moduleBar]);
So exports for Angular modules and classes should go hand in hand, they are not interchangeable.

Categories

Resources