Dependency injection using InversifyJs with an Express controller base and subsclass - javascript

My first goal is to avoid repeating myself. I am creating a Node.js express server. I want to create several class controllers each with their own route, and all with the exact same CRUD functionality: GET a single record, GET all the records, UPDATE a single record, DELETE a record, POST a new record for each data object in my database, but I want to be able to extend these controllers to add additional functionality on top of these.
My second goal is to use dependency injection to use a database service in these controllers.
The problems are the TypeScript compiler gets upset when I inject it in the base class constructor. It now wants me to add it to the subclass constructor,
// THE BASE CLASS
import { inject } from "inversify";
import db from "../db";
export default class Controller {
protected _db: db;
public path: string;
public router = Router();
constructor(path: string, #inject(db) databbase: db) {
this._db = databbase;
this.path = path; // path for my special record
this.initializeRoutes();
}
public initializeRoutes(): void {
this.router.get(this.path + '/:id', this.getRecordById);
}
getRecordById = async (req: Request, res: Response): Promise<boolean> => {
const { rows } = await this._db.query('SELECT * FROM issues WHERE id = $1', [req.params.id]);
res.send(rows.pop());
return Promise.resolve(true);
}
}
// THE SUBCLASS
import { inject } from "inversify";
import db from "../db";
import Controller from "./Controller";
export default class SubController extends Controller {
constructor(path: string, #inject(db) _db: db) { // <-- Do I have to inject it here, as well?
super(path, _db);
}
// I will add additional methods here, unique to my SubController
}
then when I need to use that class, it now wants me to fill in the second argument, the db part of the sub-class constructor.
In the documentation they give an example which implies I don't even need to use the #inject keyword, but that doesn't make sense to me. Ultimately, I have to put something in that constructor, don't I? When I finally go new IssueController('/path', [#inject db something here]), won't I need to put something in where #inject is?
My Questions
Do I need to use #inject in both the base class and the sub-class?
What do I insert when I need to call new?
Ultimately, it seems like I'm doing this wrong. Can you point me in the right direction?

Related

Extend NestJS decorator

I stumbled across this question but I don't think I want to use an alias
I want to extend the express anyFilesInterceptor so I can work with a custom file object. I am not sure how to extend a decorator in NestJS.
So as a work around I tried decorator composition from another question. However, I am getting an error just trying to create a very basic (example in documentation) decorator
import { applyDecorators, createParamDecorator, ExecutionContext } from "#nestjs/common";
import { AnyFilesInterceptor } from "#nestjs/platform-express";
export function Test() {
return applyDecorators(
AnyFilesInterceptor,
TestDecorator
)
}
export const TestDecorator = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
Now I can see from other discussions and the function naming that AnyFilesInterceptor is a mixin that returns a class while TestDecorator created by createParamDecorator likely only works on parameters.
Does NestJS have a way to create a class decorator? Or to extend existing decorators?
actually the AnyFilesInterceptor is a function itself that produces an interceptor (which is any class that implements NestInterceptor).
You can see it by the usage: while 'other' interceptors may be used by simply giving the class to the UseInterceptor() decorator, this interceptor needs invocation (without new keyword).
Example:
#UseInterceptor(RegularInterceptor)
//or
#UseInterceptor(new RegularInterceptor())
// AnyFilesInterceptor is a function returning a class
#UseInterceptor(AnyFilesInterceptor())
//or
#UseInterceptor(new (AnyFilesInterceptor())({/* some multer options here */))
so basically if you want to extend the AnyFilesInterceptor you simply need to define a class inteceptor of your own:
export class MyAllFilesInterceptor extends AnyFilesInterceptor() {
// YOU MUST OVERRIDE THE `intercept` METHOD!
// also, give options to the `AnyFilesInterceptor` method if you wish
}

How to bind instance to inversify container

I'm using inversify with inversify-express-utils.
I have a quite usual inversify/express setup.
Controller
Service
A module
B module
B module is a class that looks like this:
import { injectable } from 'inversify';
import SpellCorrector from 'spelling-corrector';
#injectable()
export class SpellCorrectorFactory {
private corrector: any;
constructor() {
this.corrector = new SpellCorrector();
this.corrector.loadDictionary();
}
public correct = (text: string): string => this.corrector.correct(text);
}
Now, the problem here is that as you can see in the constructor, I have this line of code:
this.corrector.loadDictionary()
This line takes over a second to execute.
So basically it seems like the actual instance creation is happening when I #inject service B to service A
So every time I make a request, the constructor of SpellCorrectorFactory is being executed, so the request takes over 1000ms instead of below 100ms.
How can I bind this class to inversify so that during binding the class is being instantiated and in the A module I have access to the instance which was created on app start, and not when I send a request to the express path?
Thanks in advance!
Ok, just in case someone looks at this page in search for an answer.
The solution is as simple as:
container
.bind<SpellCorrectorFactory>(TYPES.SpellCorrector)
.to(SpellCorrectorFactory)
.inSingletonScope();
This calls the constructor of the SpellCorrectorFactory immediately and returns the instance.
So whenever you inject the result of this binding you have direct access to the instance and its functions.

Why isn't my component object getting updated itself when anything in my globally shared service updates? Angular

I have this service:
export class RecipeService{
selectedRecipe: Recipe = 'xyz';
}
I have this component using this service:
export class RecipesComponent implements OnInit {
selectedRecipe: Recipe;
constructor(private recipeService: RecipeService) { }
ngOnInit(): void {
this.selectedRecipe = this.recipeService.selectedRecipe;
}
}
The service is defined in app.module.ts for injection, which means all components get the same instance.
My question is, whenever I update the selectedRecipe variable in one of my components, it doesn't get updated back in other components although it is referenced and hence I expect a change immediately.
What am I doing wrong?
It doesn't get updated because the new value is not "sent" to the already initiated angular components.
Instead you should use observables.
for example:
/* service */
private recipe = "xyz";
public recipeSubject: BehaviorSubject<string> = new BehaviorSubject(this.recipe);
// when changing the recipe
recipeSubject.next(this.recipe);
/* component */
this.service.recipeSubject.subscribe(res => this.recipe = res);
I googled and found out in one of the posts that its because of my object. The object (Recipe) in my service contains a primitive type, i.e string. If your object contains a primitive type, it isn't passed as a reference, hence a change in service object won't be reflected in component because they are now different.
Although I must clear that in case of array it worked perfectly fine even when my array contained objects which had primitive types. Changes were still reflected.

Angular 2 service calls method from component

Is it even possible to let a service call an component Method?
myapp.component
export class MyAppComponent {
public value;
...
public setValue(payload){
this.value = payload;
}
}
myapp.service
#Injectable()
export class MyAppService {
private myAppComponent: MyAppComponent;
private apiClientService: ApiClientService
// ...
After i make an PUT http call, the body from the response is my new "value"
// ...
putValue(payload: JSON){
return this.apiClientService.putAPIObject(payload).then((response) => {
this.myAppComponent.setValue(response);
});
}
}
This results in an ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'setValue' of undefined.
Can someone explain what im doing wrong?
Thanks in advance.
EDIT:
Since people complain about my approach, im totally fine to start from scratch if someone can explain me what is the best way to handle this problem.
I get values from an api, change them and i put them back to the api. I dont want to make a get call again, so i get the new data i want in the response of the Put call.
The call goes from component --> component service --> apiclient service
I guess the problem is that i have an extra service between the start and end point.
EDIT 2: I tried to avoid the component service and maked it work for me with only component --> apiclient service
Even this soultion is working for me at the moment I kind of dislike it, because I have to Copy and Paste a lot of code for the Same "Operation" with other objects from my api. For example I maked it work for the Picture Component, but I also need this for my Movie Component. Usally its a bad thing if I write the same code often in a project, or not?
There are at least a couple ways to solve this, but hopefully this gives you a start. Open to feedback and corrections.
Use an Observable
Let the service own knowledge of the value changes and emit changes. The component listens to an EventEmitter on1 the service to react to value changes. (See also: Creating and returning Observable from Angular 2 Service)
MyAppService
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MyAppService {
private valueSource = new Subject<any>();
public valueUpdate$ = this.valueSource.asObservable();
putValue(payload: JSON){
return this.apiClientService.putAPIObject(payload).then((response) => {
/** here **/
this.valueUpdate$.next(response);
});
}
}
MyAppComponent
export class MyAppComponent {
public value;
private valueSubscription;
constructor(private _myAppService: MyAppService) {}
ngOnInit() {
/** and here **/
this._myAppService.valueUpdate$.subscribe((p) => this.setValue(p));
}
...
public setValue(payload){
this.value = payload;
}
}
Register the component
Answering the original question, the idea is to register the component with the service so that it can call the component as needed. You could pull a references through dependency injection but wouldn't recommend it (e.g. what if your original component reference is destroyed?)
MyAppService
#Injectable()
export class MyAppService {
private myAppComponent: MyAppComponent;
/** here **/
registerMyApp(myApp: MyAppComponent) {
this.myAppComponent = myApp;
}
putValue(payload: JSON){
return this.apiClientService.putAPIObject(payload).then((response) => {
this.myAppComponent.setValue(response);
});
}
}
MyAppComponent
export class MyAppComponent {
public value;
/** and here **/
constructor(myAppService: MyAppService) {
myAppService.registerMyApp(this);
}
...
public setValue(payload){
this.value = payload;
}
}
Thanks AJT_82 for noting that Angular does not want developers using EventEmitters on the service: What is the proper use of an EventEmitter?.

How to call a class methods in JavaScript

I am currently learning ES6. While I was playing with the new features, I got stuck at one point. How to call a class method.
So let's say I have a class in one file like below:
class Auth {
checkUserSignedIn() {
//check user signed in
}
signupUser(account) {
//signup user
}
loginUser(account) {
//login user
}
getCurentUser() {
//Current User
}
}
module.exports = Auth;
and then in some other file, let's say a controller I would like to call these functions.
const Auth = require('./auth');
class Controller {
signupUserUsingEmailAndPass(user) {
Auth.signupUser(account);
}
loginUserUsingEmailAndPass(account) {
Auth.loginUser(account);
}
isUserSignedIn() {
checkUserSignedIn();
}
}
module.exports = Controller;
But this doesn't work at all. I guess there is something I am not understanding correctly. Any suggestion/advice?
Methods defined in a class require an instance of that class, i.e. a new Auth somewhere.
Your controller should be defined has
class Controller {
constructor(auth) {
this.auth = auth;
}
}
This approach over the require('myclass') allows for you to inject different implementations of your Auth class.
Should you not desire an instance of Auth for those methods, declare them as static. More on that here
You need to either instatiate the Auth
const authorization = new Auth();
Possibly inside the file containing the Auth class, and just export the instance.
export const authorization = new Auth();
Or, if You want this methods available outside. You can make the methods static. Just add static keyword before the method name during method creation.
You can read more about static methods here
Javascript classes don't work quite like the module pattern (that it seems) you're used to. When you export `Auth, you're exporting the class definition but you still need to instantiate it.
let auth = new Auth()
auth.signupUserUsingEmailAndPass() // or any other function you define
In javascript, a class is useful when you want to populate it with data or some sort of state. If you don't want or need that then you can use a module.

Categories

Resources