Vue2: The right way to include / inject a global utility class - dependency-injection

I have Vue.js project where I would like to use a utility class for functions I want to use in all my modules.
modules/Utils.js
export default class Utils {
array_count (arr) {
return Array.isArray(arr) ? arr.length : 0;
}
}
main.js
import Vue from 'vue';
import Utils from 'modules/Utils';
export default new Vue({
el: '#app',
router,
template: '<App/>',
components: {
App
},
utils: Utils // this has no affect?
});
modules/some.js
import Vue from 'vue';
import Utils from 'modules/Utils'; // I don't want this
var utils = new Utils; // I don't want this
console.log(utils.array_count(['a','b','c']));
I don't really understand how to inject the Utils Class. The example above works - but I would like to get rid of the import of the Class in each module. I thought when I add it in main.js as a dependency, I should be able to call it anywhere in my project.

when you want to access the helpers from every component you could register a global mixin.
Change your Utils.js to look like a Mixin.
export default {
methods: {
array_count (arr) {
return Array.isArray(arr) ? arr.length : 0;
}
}
}
Then, import the mixin and add it globally.
import Vue from 'vue';
import Utils from 'modules/Utils';
Vue.mixin(Utils);
export default new Vue({
el: '#app',
router,
template: '<App/>',
components: {
App
},
Alternatively, if you want some helper functions not to be available globally but just in some components, you could import the mixin and add it manually to Vue components.

Related

Using BullMQ with NestJs, is it possible to use a config variable as part of a Job name?

I'm trying to use an environment variable value in the nestjs/bull module's #Process() decorator, as follows. How should I provide the 'STAGE' variable as part of the job name?
import { Process, Processor } from '#nestjs/bull';
import { Inject } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { Job } from 'bull';
#Processor('main')
export class MqListener {
constructor(
#Inject(ConfigService) private configService: ConfigService<SuperRootConfig>,
) { }
// The reference to configService is not actually allowed here:
#Process(`testjobs:${this.configService.get('STAGE')}`)
handleTestMessage(job: Job) {
console.log("Message received: ", job.data)
}
}
EDITED with answers (below) from Micael and Jay:
Micael Levi answered the initial question: You can't use the NestJS ConfigModule to get your config into a memory variable. However, running dotenv.config() in your bootstrap function will not work either; you get undefined values for the memory variables if you try to access them from within a Method Decorator. To resolve this, Jay McDoniel points out that you have to import the file before you import AppModule. So this works:
// main.ts
import { NestFactory } from '#nestjs/core';
require('dotenv').config()
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT || 4500);
}
bootstrap();
You cannot use this on that context due to how decorator evaluation works. At that time, there's no instance created for MqListener class, thus, using this.configService doens't make sense.
You'll need to access process.env. directly. And so will call dotenv (or what lib that read & parses your dot env file) in that file.

Efficient and simplest way to insert template based content into generated files with Yeoman generator

Suppose I already have some files generated by a generator and want to create some sub-generators that inserts contents into these files based on some content's template.
The goal is to create a generator of a multilayer architecture composed by 3 layers (for Angular2 app written in typescript):
applicatif layer
metier layer and
business-delegate layer
For each layer, the main generator have to generate all files composing it: a module file, interfaces files, ... The main 3 files generated in this process looks like this:
hero.applicatif.ts:
import { Injectable } from '#angular/core';
import { IHeroApplicatif } from './hero.applicatif.interface';
import { HeroMetier } from '../metier/hero.metier';
#Injectable()
export class HeroApplicatif implements IHeroApplicatif {
constructor(private heroMetier: HeroMetier) {}
}
hero.metier.ts:
import { Injectable } from '#angular/core';
import { IHeroMetier } from './hero.metier.interface';
import { HeroBusinessDelegate } from '../business-delegate/hero.business-delegate';
#Injectable()
export class HeroMetier implements IHeroMetier {
constructor(private heroBusinessDelegate: HeroBusinessDelegate) {}
}
hero.business-delegate.ts:
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { IHeroBusinessDelegate } from './hero.business-delegate.interface';
#Injectable()
export class HeroBusinessDelegate implements IHeroBusinessDelegate {
constructor(private http: Http) {}
}
Generating these files based on templates doesn't pose problem. But I want sub-generators that prompt the user to input a method name, it's return type and parameters so the sub-generator have to modify each previously generated files to inserts codes that, by default, for each layer, pass the call to the next layer.
Suppose the sub-generator have prompt the user to input a method called getHero, the contents of the 3 files have to be modified like this:
hero.applicatif.ts:
import { Injectable } from '#angular/core';
import { IHeroApplicatif } from './hero.applicatif.interface';
import { HeroMetier } from '../metier/hero.metier';
#Injectable()
export class HeroApplicatif implements IHeroApplicatif {
constructor(private heroMetier: HeroMetier) {}
getHero(id:number): Promise<any> {
return this.heroMetier.getHero(id);
}
}
hero.metier.ts:
import { Injectable } from '#angular/core';
import { IHeroMetier } from './hero.metier.interface';
import { HeroBusinessDelegate } from '../business-delegate/hero.business-delegate';
#Injectable()
export class HeroMetier implements IHeroMetier {
constructor(private heroBusinessDelegate: HeroBusinessDelegate) {}
getHero(id:number): Promise<any> {
return this.heroBusinessDelegate.getHero(id);
}
}
hero.business-delegate.ts:
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { IHeroBusinessDelegate } from './hero.business-delegate.interface';
#Injectable()
export class HeroBusinessDelegate implements IHeroBusinessDelegate {
constructor(private http: Http) {}
getHero(id:number): Promise<any> {
return this.http.get(...).toPromise();
}
}
What is the simplest, safe, up to date way to do that?
To edit code files content in a safe way that won't mess up with un-predicted changes by the end user, the safest way is to modify a file Abstract Syntax Tree.
There's multiple AST parser available for Node. The two most popular being Esprima and Acorn. There's also a few tools built on those parser to modify AST more easily.
I wrote such a tool a while back if you want to check it out https://github.com/SBoudrias/AST-query - it might works for your use case.

How do I use material-ui in electron

My code always gave me an error in the electron console, however the same code did not give me an error in a web browser:
import React, { Component } from 'react';
import { Router, Route, Link, browserHistory ,hashHistory} from 'react-router'
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import injectTapEventPlugin from 'react-tap-event-plugin';
import { RaisedButton } from 'material-ui';
class App extends Component {
render() {
return (
<MuiThemeProvider>
<RaisedButton className="submit" label='xxxxxxx' labelColor="#fff" backgroundColor="#32a62e"></RaisedButton>
</MuiThemeProvider>
)
}
}
injectTapEventPlugin();
let rootElement = document.getElementById('containers');
render(
<App/>,
rootElement
)
You need to add
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
to the top level of your javascript files as explained in this page of the documentation . It's because there is a dependencey on this module for onTouchTap to work So move it up before the class definition or to the top js file you have.

Custom ViewResolver and dependency injection

I'm attempting to create a custom ViewResolver class (extending angular's built-in class) to augment my systems' style metadata with a custom service (I'm loading my styles from external systems). However I'm running into a problem with the dependency injection system and ViewResolver.
I have my system setup something like the following:
Boot.ts:
bootstrap(App, [
MyStyleService, // my custom service
SomeOtherService, // another custom service used by MyStyleService
{
provide: ViewResolver,
useClass: MyViewResolver // my custom ViewResolver
}
])
MyViewResolver.ts:
#Injectable()
export class MyViewResolover extends ViewResolver {
constructor(
private _reflector: ReflectorReader,
// I want to reference 'StyleService' from the providers array in boot.ts
private _styleService: StyleService
) {
super(_reflector);
}
public resolve(comopnent: Type) {
let meta: ViewMetadata = super.resolve(component);
let styles = this._styleService.someMethod(meta);
}
}
However inside MyViewResolver, this._styleService has NOT been injected and is currently undefined. It should be noted that MyStyleService also depends on another injected service SomeOtherService, so I need to make sure that that provider is also defined and available for the injector.
I want all of these services to be "provided" by the bootstrap, so that in the future I can provide alternate versions of any of my services on a per-system basis.
For reference this is angular's core ViewResolver:
view_resolver.ts (Angular2 core):
import {Injectable, ViewMetadata, ComponentMetadata,} from '#angular/core';
import {ReflectorReader, reflector} from '../core_private';
import {Type, stringify, isBlank, isPresent} from '../src/facade/lang';
import {BaseException} from '../src/facade/exceptions';
import {Map} from '../src/facade/collection';
#Injectable()
export class ViewResolver {
constructor(private _reflector: ReflectorReader = reflector) {}
resolve(component: Type): ViewMetadata {
... stuff here ...
}
}
You could try to configure your class with useFactory:
bootstrap(App, [
MyStyleService,
SomeOtherService,
{
provide: ViewResolver,
useFactory: (_reflector: ReflectorReader, styleService: StyleService) => {
return MyViewResolver(_reflector, _styleService);
},
deps: [ ReflectorReader, StyleService ]
}
]);

Angular2 - Use Dependency Injection outside Components

In my app I use DI to pass information about the UserLogged around the different Components that need such info.
This means that I have a main.ts class like this
import {AppComponent} from './app.component';
import {UserLogged} from './userLogged'
bootstrap(AppComponent, [UserLogged]);
and the components that need to use the instance of UserLogged have a constructor like this
constructor(private _user: UserLogged)
Now I would like to use the same instance of UserLogged also in simple TypeScript classes (which are not #Component). Is this possible? In other words, can I get hold of the same instance of UserLogged injected by DI also if I am outside a #Component?
This constructor also works for services (other classes created by DI)
bootstrap(AppComponent, [OtherClass, UserLoggged]);
#Injectable()
export class UserLogged {
log(text) {
console.log(text);
}
}
#Injectable()
export class OtherClass {
constructor(private _user: UserLogged) {}
}
class SomeComponent {
constructor(private otherClass:OtherClass) {
this.otherClass._user.log('xxx');
}
}
If you create these classes using new SomeClass() then you can inject it like
class SomeComponent {
constructor(private _injector:Injector) {
let userLog = this._injector.get(UserLogged);
new SomeClass(userLog);
}
}
In the file where you bootstrap angular:
import { AppComponent } from './app.component';
import { UserLogged } from './userLogged';
declare global {
var injector: Injector;
}
bootstrap(AppComponent, [UserLogged]).then((appRef) => {
injector = appRef.injector;
});
In your other file:
import { UserLogged } from '../path/to/userLogged';
class TestClass {
private userLogged: UserLogged;
constructor() {
this.userLogged = injector.get(UserLogged);
}
}
To be able to use dependency injection in classes you need to have a decorator, the #Injectable one.
#Injectable()
export class SomeClass {
constructor(private dep:SomeDependency) {
}
}
The name Injectable isn't really self-explanatory. It's to make possible the injection within the class (and not into another class).
The provider for the SomeDependency class need to be visible from the component that initiates the call.
See this question for more details about dependency injection and hierarchical injectors:
What's the best way to inject one service into another in angular 2 (Beta)?
Im taking a wild guess here, but do you mean to say you want to Inject the class with having to use providers : [UserLogged] ?
If that is the case, this will work
providers: [ provide(UserLogged, {useClass: UserLogged} ]
add the above to your bootstrap and you are good to go when you 'do not want to use #Component'
sample.ts
export class Sample{
constructor(private ulog : UserLogged){}
}
In your case the bootstrap would be :
import {provide} from 'angular2/core';
import {HTTP_PROVIDERS} from 'angular2/http';
bootstrap(AppComponent,[HTTP_PROVIDERS,provide(UserLogged, { useClass : UserLogged})]);
Ive added the HTTP_PROVIDERS to demonstrate how to add multiple providers.
Cheers!

Resources