How to bind instance to inversify container - javascript

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.

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
}

Using class object from diffrent class

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.

Get exported singleton instance from string in javascript

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.

Executing some code when a service is initialized

Is it possible to execute some code when a service is initialized. For example when the service product Service is initialized I would want to execute this code:
this.var = this.sharedService.aVar;
Other than constructor there is NO lifecycle hooks for Service..
Lifecycle hooks are supported by component/directive
Injectables are normal classes (normal objects) and as such, they have no special lifecycle.
#Injectable()
export class SampleService {
constructor() {
console.log('Sample service is created');
//Do whatever you need when initialized.
}
}
the class’s constructor is called, so that’s what your “OnInit” would be. As for the destruction, a service does not really get destroyed.
where a service needs another service use dependency injection
#Injectable()
export class HeroService {
private yourVariable: any;
constructor(private sharedService: sharedService) {
this.yourVariable = this.sharedService.aVar;
}
I suggest you read up a little on lifecycle hooks in Angular. While in the constructor is an option it is a good idea to keep the code in that limited to variable initialization and use the ngOnInit lifecycle hook to do class initialization work. Both are an option but understanding the lifecycle hooks is a good starting point in addressing your question and thinking through where you want to do this.

How to expose angular 2 methods publicly?

I am currently working on porting a Backbone project to an Angular 2 project (obviously with a lot of changes), and one of the project requirements requires certain methods to be accessible publicly.
A quick example:
Component
#component({...})
class MyTest {
private text:string = '';
public setText(text:string) {
this.text = text;
}
}
Obviously, I could have <button (click)="setText('hello world')>Click me!</button>, and I would want to do that as well. However, I'd like to be able to access it publicly.
Like this
<button onclick="angular.MyTest.setText('Hello from outside angular!')"></click>
Or this
// in the js console
angular.MyTest.setText('Hello from outside angular!');
Either way, I would like the method to be publicly exposed so it can be called from outside the angular 2 app.
This is something we've done in backbone, but I guess my Google foo isn't strong enough to find a good solution for this using angular.
We would prefer to only expose some methods and have a list of public apis, so if you have tips for doing that as well, it'd be an added bonus. (I have ideas, but others are welcomed.)
Just make the component register itself in a global map and you can access it from there.
Use either the constructor or ngOnInit() or any of the other lifecycle hooks to register the component and ngOnDestroy() to unregister it.
When you call Angular methods from outside Angular, Angular doesn't recognize model change. This is what Angulars NgZone is for.
To get a reference to Angular zone just inject it to the constructor
constructor(zone:NgZone) {
}
You can either make zone itself available in a global object as well or just execute the code inside the component within the zone.
For example
calledFromOutside(newValue:String) {
this.zone.run(() => {
this.value = newValue;
});
}
or use the global zone reference like
zone.run(() => { component.calledFromOutside(newValue); });
https://plnkr.co/edit/6gv2MbT4yzUhVUfv5u1b?p=preview
In the browser console you have to switch from <topframe> to plunkerPreviewTarget.... because Plunker executes the code in an iFrame. Then run
window.angularComponentRef.zone.run(() => {window.angularComponentRef.component.callFromOutside('1');})
or
window.angularComponentRef.zone.run(() => {window.angularComponentRef.componentFn('2');})
This is how i did it. My component is given below. Don't forget to import NgZone. It is the most important part here. It's NgZone that lets angular understand outside external context. Running functions via zone allows you to reenter Angular zone from a task that was executed outside of the Angular zone. We need it here since we are dealing with an outside call that's not in angular zone.
import { Component, Input , NgZone } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'example',
templateUrl: './example.html',
})
export class ExampleComponent {
public constructor(private zone: NgZone, private router: Router) {
//exposing component to the outside here
//componentFn called from outside and it in return calls callExampleFunction()
window['angularComponentReference'] = {
zone: this.zone,
componentFn: (value) => this.callExampleFunction(value),
component: this,
};
}
public callExampleFunction(value: any): any {
console.log('this works perfect');
}
}
now lets call this from outside.in my case i wanted to reach here through the script tags of my index.html.my index.html is given below.
<script>
//my listener to outside clicks
ipc.on('send-click-to-AT', (evt, entitlement) =>
electronClick(entitlement));;
//function invoked upon the outside click event
function electronClick(entitlement){
//this is the important part.call the exposed function inside angular
//component
window.angularComponentReference.zone.run(() =
{window.angularComponentReference.componentFn(entitlement);});
}
</script>
if you just type the below in developer console and hit enter it will invoke the exposed method and 'this works perfect ' will be printed on console.
window.angularComponentReference.zone.run(() =>
{window.angularComponentReference.componentFn(1);});
entitlement is just some value that is passed here as a parameter.
I was checking the code, and I have faced that the Zone is not probably necessary.
It works well without the NgZone.
In component constructor do this:
constructor(....) {
window['fncIdentifierCompRef'] = {
component = this
};
}
And in the root script try this:
<script>
function theGlobalJavascriptFnc(value) {
try {
if (!window.fncIdentifierCompRef) {
alert('No window.fncIdentifierCompRef');
return;
}
if (!window.fncIdentifierCompRef.component) {
alert('No window.fncIdentifierCompRef.component');
return;
}
window.fncIdentifierCompRef.component.PublicCmpFunc(value);
} catch(ex) {alert('Error on Cmp.PublicCmpFunc Method Call')}
}
</script>
This works to me.
The problem is that Angular's components are transpiled into modules that aren't as easy to access as regular JavaScript code. The process of accessing a module's features depends on the module's format.
An Angular2 class can contain static members that can be defined without instantiating a new object. You might want to change your code to something like:
#component({...})
class MyTest {
private static text: string = '';
public static setText(text:string) {
this.text = text;
}
}
Super simple solution!! save component or function with an alias outside
declare var exposedFunction;
#Component({
templateUrl: 'app.html'
})
export class MyApp {
constructor(public service:MyService){
exposedFunction = service.myFunction;
}
at index.html add in head
<script>
var exposedFunction;
</script>
Inside exposed function do not use this. parameters if you need them you will have to use closures to get it to work
This is particularly useful in ionic to test device notifications on web instead of device

Categories

Resources