Dependency Injection Constructor that takes arguments - javascript

I'm building an Aurelia app that uses "Models" for every type of data object.
All my Models look something like this:
export class Item {
id = null;
name = '';
description = '';
constructor (data) {
Object.assign(this, data);
}
}
And I later create objects like this:
export class SomeViewModel {
activate () {
this.myItem = new Item({
name: 'My Item!',
description: 'This is my item. It will be initialized with these properties.'
});
}
}
I got the Object.assign() bit from an article I read and it works really well. It allows me to create new items using data from the server, or if I want an empty Item I simply don't pass in anything.
Now I've reached a point where I need my model to have access to another class so I'm using Aurelia's Dependency Injection like this:
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(Router)
export class Item {
id = null;
name = '';
description = '';
constructor (router, data) {
this.router = router;
Object.assign(this, data);
}
get permalink () {
return window.location.protocol + '//' + window.location.host + this.router.generate('item', {itemId: this.id});
}
}
Now my problem is this; how do I create a new Item() without passing in the Router myself? I guess switching the order of argument to constructor() would do the trick but that doesn't seem to work with Aurelia?
And I don't want to have to do this every time I create a new Item:
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(Router)
export class SomeViewModel {
constructor (router) {
this.router = router;
}
activate () {
this.myItem = new Item(this.router, {
name: 'My Item!',
description: 'This is my item. It will be initialized with these properties.'
});
}
}
Surely there must be a better way to solve this?

Use the Factory resolver. Here's an example: https://gist.run?id=46642ac54893186067e7cd890d6722a3**
import {inject, Factory} from 'aurelia-dependency-injection';
import {MyModel} from './my-model';
#inject(Factory.of(MyModel))
export class App {
message = 'Hello World!';
constructor(createModel) {
let model = createModel('my data');
}
}
my-model.js
import {inject} from 'aurelia-dependency-injection';
import {EventAggregator} from 'aurelia-event-aggregator';
#inject(EventAggregator)
export class MyModel {
constructor(eventAggregator, data) {
console.log(eventAggregator, data);
}
}

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 create a singleton observable?

I have a singleton class :
let instance = null;
class SingletonClass {
constructor() {
if (!instance) {
instance = this;
}
return instance;
}
get types() {
return this._types;
}
set types(value) {
this._types = value;
}
}
export default SingletonClass;
and i use it like this:
import SingletonViews from './SingletonClass';
export default class MyStore {
this._singletonClass = new SingletonClass();
#observable types;
this.types= this._singletonClass.types; //i dont know what to do here to bind this two together
}
But whenever this._singletonClass.types updated the types observable wont. what can i do about it?
can i have a #observable singleton or can i bind its proprty to a #observable
Your "singleton" is not an observable itself and what is your intention? just proxy the singleton values?
Since you don't seem to need to initialize your singleton, I'd rewrite it this way:
Singleton
import * as mobx from 'mobx'
const {observable, action} = mobx
class SingletonClass {
#observable types
#action setTypes (value) {
this.types = value;
}
}
export default new SingletonClass()
MyStore
import singletonClass from './SingletonClass'
export default class MyStore {
singletonClass = singletonClass
}
Now you can use your store somewhere in your app and observe your singleton.

Mocking Vue class component methods with inject loader

Let's say I have a very basic vue-class-component as shown below:
<template>
<div>Nothing of interest here</div>
</template>
<script>
import Vue from 'vue';
import Component from 'vue-class-component';
import http from './../modules/http';
#Component
export default class Example extends Vue {
user = null;
errors = null;
/**
* #returns {string}
*/
getUserId() {
return this.$route.params.userId;
}
fetchUser() {
this.user = null;
this.errors = null;
http.get('/v1/auth/user/' + this.getUserId())
.then(response => this.user = response.data)
.catch(e => this.errors = e);
}
}
</script>
I want to test the fetchUser() method so I just mock the './../modules/http' dependency and make http.get return a Promise. The problem is that in my assertion I want to check if the URL is being built properly and in order to do so the user ID has to come from an hard-coded variable in the test.
I tried something like this but it doesn't work:
import ExampleInjector from '!!vue-loader?inject!./../../../src/components/Example.vue';
const mockedComponent = ExampleInjector({
'./../modules/http': {
get: () => new Promise(/* some logic */)
},
methods: getUserId: () => 'my_mocked_user_id'
});
Unfortunately it doesn't work and I could not find anything this specific in the Vue docs so the question is, how am I supposed to mock both external dependencies and a class component method?
NOTE: I do not want to mock this.$route.params.userId as the userId could potentially come from somewhere else as well (plus this is just an example). I just want to mock the getUserId method.
Since I specifically asked about how I could mock Vue class component methods with the inject loader here's the complete solution for the question at hand:
import ExampleInjector from '!!vue-loader?inject!./../../../src/components/Example.vue';
const getComponentWithMockedUserId = (mockedUserId, mockedComponent = null, methods = {}) => {
if (!mockedComponent) {
mockedComponent = ExampleInjector();
}
return Vue.component('test', {
extends: mockedComponent,
methods: {
getUserId: () => mockedUserId,
...methods
}
});
};
And then in my test case:
const component = getComponentWithMockedUserId('my_mocked_user_id');
const vm = new Vue(component);
I found this to be very helpful when you need to create a partial mock AND inject some dependencies too.
The easiest way to do this is to extend the component and override the method:
const Foo = Vue.extend({
template: `<div>{{iAm}}</div>`,
created() {
this.whoAmI();
},
methods: {
whoAmI() {
this.iAm = 'I am foo';
}
},
data() {
return {
iAm: ''
}
}
})
Vue.component('bar', {
extends: Foo,
methods: {
whoAmI() {
this.iAm = 'I am bar';
}
}
})
new Vue({
el: '#app'
})
In this example I'm using the extends property to tell Vue that Bar extends Foo and then I'm overriding the whoAmI method, you can see this is action here: https://jsfiddle.net/pxr34tuz/
I use something similar in one of my open source projects, which you can check out here. All I'm doing in that example is switching off the required property for the props to stop Vue throwing console errors at me.

React native and MobX: How to create a global store?

I am currently trying to implement a mobx storage which I can call from everywhere like so:
import {userStore} from '../UserStore.js';
At the end of the file, my export functionality looks as follows:
const userStore = new UserStore();
export {userStore};
As I understand, everytime I call the import functionality, the object gets re-created with which multiple files that import UserStore don't share the same variables.
However, I want that every file that imports UserStore imports the same object with the exact same variables. How can I achieve this? I am not fully sure how to achieve, so any ideas and examples would be appreciated :)
The full code (for the UserStore.js declaration), if of any help, is as follows (look at the very bottom for the export statement)
import {observable, computed, action} from 'mobx';
import {ObservableMap, toJS} from 'mobx';
import {Fb} from './firebase.js';
class UserStore {
/** GPS */
#observable usrLng = 0.0;
#observable usrLat = 0.0;
#observable watchID = null;
#action
watchCurLocation() {
this.watchID = navigator.geolocation.watchPosition((position) => {
console.log("Recording GPS data from within the Store!!");
this.usrLat = parseFloat(position.coords.latitude);
this.usrLng = parseFloat(position.coords.longitude);
}, (error) => console.log(JSON.stringify(error)), {
enableHighAccuracy: true,
timeout: 2000,
maximumAge: 1000
});
}
#action
clearWatch() {
navigator.geolocation.clearWatch(this.watchID);
}
/*/ GPS */
/** BIKE BOOKING */
#observable interestBikeNo = -1;
#observable bookedBikeNo = -1;
#action
setInterestBikeNo(bn) {
this.interestBikeNo = bn;
}
}
const userStore = new UserStore();
export {userStore};
You simply need a singleton instance of UserStore class
Sample demo
let localInstance = null;
export class Apple {
static newInstance() {
if (! localInstance)
localInstance = new Apple();
return localInstance;
}
}
// usage
import {Apple} from './apple';
const instance = Apple. newInstance();
In your case, you can use a simple function
import {observable, computed, action} from 'mobx';
import {ObservableMap, toJS} from 'mobx';
import {Fb} from './firebase.js';
class UserStore {
// omitted
}
let userStore;
export function getUserstore() {
if (!userStore)
userStore = new UserStore();
return userStore;
};
Somewhere in code
// instead of
import {userStore} from './someUserStoreModule';
// use
import {getUserstore} from './someUserStoreModule';
const userStore = getUserstore();

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