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.
Related
When I click the "back button", my Uppy form is briefly loaded twice. How can I get Stimulus to close the previous Uppy instance when I leave the page?
Following the Uppy Docs, I'm hoping something like this (code below) but I get the error: uppy is nil or undefined on disconnect().
How can I bind uppy.close() to the original instance of Uppy?
import { Controller } from "#hotwired/stimulus"
import Uppy from "#uppy/core"
export default class extends Controller {
connect() {
const uppy = new Uppy()
}
disconnect() {
// This causes an error
uppy.close();
}
}
The problem is that you are trying to access uppy as a local variable that you have set in the connect method. However, methods to get get access to other methods' scope, which is why uppy is undefined when you try to access it in the disconnect method.
Instead, you have a few options.
Option 1 - Store the variable on the class instance
Each class instance has its own this variable, and you an easily store values on it as it is an object.
By convention, the below example uses this._uppy (with an underscore) but any name will work.
import { Controller } from "#hotwired/stimulus";
import Uppy from "#uppy/core";
export default class extends Controller {
connect() {
// 'store' the uppy instance against your class instance
this._uppy = new Uppy();
}
disconnect() {
// read the instance and access its close method
this._uppy.close();
}
}
Option 2 - Store the variable globally
Another way to think about this is that you do not want to create a new Uppy instance if there is already one created.
You can store this instance on window, although this breaks the concept of your file being an isolated module.
Another way is to store the instance in the module file itself, this may create some edge case issues for unit tests but see how you go.
import { Controller } from "#hotwired/stimulus";
import Uppy from "#uppy/core";
// declare a variable in the 'module' scope (this file).
let uppy;
export default class extends Controller {
connect() {
// if uppy has already been instantiated - do not create a new one
if (uppy) return;
// 'store' the uppy instance against your module scoped global variable
uppy = new Uppy();
}
disconnect() {
// read the instance and access its close method
uppy && uppy.close();
// reset the variable to null
uppy = null;
}
}
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
}
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?
I’m having type script class which expose some service list
I’ve defined a property that should have reference to the ServiceCatalog class like following:
export default class myGenerator extends Generator {
private svcat: ServiceCatalog | undefined;
// and here I’ve initilzied the property
await this.getSvc();
// here I created some function the return service instances
private async getSvc() {
this.svcat = new ServiceCatalog();
await this.svcat.getServiceInstances();
}
// and here I’ve additional code which use it
this.svcat.values ….
My question is there Is a better/cleaner way of doing the in javascript/typescript ?
maybe not using the this keyword...
And also maybe a better code for testing (unit-test) ...
The way you are doing today, it is very hard to test. Why is that? Well, because if you want to isolate your Generator class from your ServiceCatalog, you will have a hard time.
What I suggest, like the colleague above, is to have the ServiceCatalog coming by customer BUT have a default value.
class MyGenerator extends Generator {
constructor(private svCat: ServiceCatalog = new ServiceCatalog()) {
super();
}
}
This way you can use it normally like
new MyGenerator()
or for testing
new MyGenerator(myFakeServiceCatalog)
Inject the Service into your myGenerator class.
Add this to your constructor:
constructor(private svcat:ServiceCatalog) {}
You can now access the injected Service using
await this.svcat.getServiceInstances();
There is no need to add a property (your svcat:ServiceCatalog|undefined part) for the service.
"this" is needed a lot in java/type-script since it refers to the current class.
I have an ExampleView class which should be singleton. Inside this class I have method render which I would like to call from another js file via object name 'ExampleView'.
Here is the code:
import View from './view';
class ExampleView extends View {
render() {
return `<span>Test</span>`;
}
}
export default new ExampleView(); //<- exporting as singleton and this object I try to get via it's name
First I should do is retrieve somehow exported singleton object ExampleView from name 'ExampleView' and then call method render.
class Test {
getInstanceOfModuleByName(name) {
//window[name].render();
}
}
The problem is that I cannot find any way to get ExampleView instance from name, but my ExampleView class needs to be a singleton.
I know I can do this in following way and then call it by window['ExampleView'].render():
ExampleView = {
render: function() {
return `<span>Test</span>`;
}
}
But I really need to implement it with modulable ES6.
Can you explain me what I should do?
Populating global scope is not the ES6 way. I think you can do it like this to your export
//
// Class definition
//
window.ExampleView = new ExampleView();
export default window.ExampleView;
You just import the instance
import whateverYouWantToCallYourViewInstance from './ExampleView'
// snip
return whateverYouWantToCallYourViewInstance.render();
It will be the same object.
Worth noting: Singletons are evil. Using the ES module system to pass instances around is a bit of abuse.