Aurelia: Dep. Injection on derived classes not possible? (or what am I doing wrong?!) - aurelia

The scenario: I have two derived classes that both extend the ActionBase class as follows. I want to use DI for both derived classes. But both classes have different dependencies. That should be possible, right? So what am I doing wrong? In both cases the injected instances/modules are 'undefined'. Any help/hint appreciated.
/*
* Base class for Actions
*/
export class ActionBase {
type;
constructor(type) {
this.type = type;
}
}
/*
* Derived Class: InsertAction
*/
import {inject} from 'aurelia-framework';
import {ActionBase} from './ActionBase';
import {PomManager} from '../manager/PomManager';
#inject(PomManager)
export class InsertAction extends ActionBase {
pomManager;
constructor(pomManager) {
super("insert");
this.pomManager = pomManager;
console.log("[InsertAction:constructor] pomManager: ", this.pomManager); // undefined
}
}
/*
* Derived Class: RenderAction
*/
import {inject} from 'aurelia-framework';
import {ActionBase} from './ActionBase';
import {AnotherManager} from '../manager/AnotherManager';
#inject(AnotherManager)
export class RenderAction extends ActionBase {
anotherManager;
constructor(anotherManager) {
super("render");
this.anotherManager = anotherManager;
console.log("[RenderAction:constructor] anotherManager: ", this.anotherManager); // undefined
}
}

It is supported. Look at this working example where Action1 and Action2 extend BaseAction and each take different dependencies.
Here's an example: https://gist.run?id=0efabf77c649f41981dcde753fdc542c
app.js
import {inject} from 'aurelia-dependency-injection'
import {Action1, Action2} from './classes'
#inject(Action1, Action2)
export class App {
constructor(a1, a2){
this.message = "look at console output";
console.log("a1", a1.dep.constructor.name);
console.log("a2", a2.dep.constructor.name);
}
}
classes.js
import {inject} from 'aurelia-dependency-injection'
export class Action1Dependency {}
export class Action2Dependency {}
export class ActionBase{
}
#inject(Action1Dependency)
export class Action1 extends ActionBase{
constructor(dep){
super();
this.dep = dep;
}
}
#inject(Action2Dependency)
export class Action2 extends ActionBase{
constructor(dep){
super();
this.dep = dep;
}
}

Related

How to mock global variable which holds React Native native module function

In App.js I have some thing like
Class App extends Component {
constructor(props){
super(props)
global.test = NativeModules.TestClass
}
}
And in Test class I am using it like
Class Test extends Component {
constructor(props){
super(props)
global.test.testFunction("Testing")
}
}
So how to mock global.test.testFunction for the above class
I suggest another approach for global function, variables. You can create a Utils class for each global functions, variables (constants or enums) and import the Utils wherever you need to use them.
Here is a very basic example for Utils Class:
Utils Class
export default class Utils {
static navigationRef = null;
static showAlert(title, desc) {
Alert.alert(title, desc);
}
}
USAGE:
import Utils from "./shared/utils"
// Example of static function
Utils.showAlert("Hello" "Alert Description")
// Example of static variable
Utils.navigationRef = this.props.navigation;

inversify.js resolve by name

I want to create little CQRS framework for my Typescript Node.js project.
I wish to resolve class by it's name from inversify.js container.
I need something like that:
let x = container.get("Foo");
where Foo is regular class and container is Container object from inversify.js.
Is it possible to do what I want?
You should be able to do the following:
import { Container, injectable } from "inversify";
#injectable()
class Foo {
// ...
}
const container = new Container();
container.bind(Foo).toSelf();
const foo = container.get(Foo);
Or (if foo is the composition root):
import { Container, injectable } from "inversify";
#injectable()
class Foo {
// ...
}
const container = new Container();
const foo = container.resolve(Foo);

Default imports with TypeScript

Using tsc 1.8.9... why are these imports not working? I thought TypeScript implemented ES6 module syntax?
"classes/person.ts"
export default class Person {
protected _name: string;
protected _language: string;
constructor(name: string) {
this._name = name;
this.hello();
}
public hello() {
console.log("Hello, " + this._name);
console.log("Lang: " + this._language);
}
}
"classes/englishman.ts"
import Person from "person"
export default class Englishman extends Person {
constructor(name: string){
this._language = "en_GB";
super(name);
}
}
"main.ts"
import * as $ from "jquery";
import Englishman from "classes/englishman";
let tom: Person = new Englishman("Tom");
console.log(tom);
$("body").html(`<h1>TEST</h1>`);
Errors:
source/main.ts(2,24): error TS2307: Cannot find module
'classes/englishman'. source/main.ts(4,10): error TS2304: Cannot find
name 'Person'. [13:53:43]
TypeScript: 2 semantic errors
After some changes, it worked for me, tested in ES5 and ES6. I hope to help you:
Original
import Person from "classes/person";
import Englishman from "classes/englishman";
Change for test
import Person from './person';
import Englishman from './classes/englishman';
maybe you need to check your directory tree.
Add
import Person from './classes/person';
.
import Person from './person'; //<-- change
export default class Englishman extends Person {
constructor(name: string){
this._language = "en_GB";
super(name);
}
}
export default class Person {
protected _name: string;
protected _language: string;
constructor(name: string) {
this._name = name;
this.hello();
}
public hello() {
console.log("Hello, " + this._name);
console.log("Lang: " + this._language);
}
}
import Englishman from './classes/englishman'; //<-- change
import Person from './classes/person'; //<-- add
class HelloWorld{
public static main(){
let tom: Person = new Englishman("Tom");
console.log(tom);
}
}
HelloWorld.main();
Hello, Tom
Lang: en_GB
Englishman { _language: 'en_GB', _name: 'Tom' }
tsc Version 1.8.2
node v5.4.1
Make sure you are compiling with ES6 target, if I'm not mistaken, ES5 is still the default target.

Centralize Aurelia validation logic

I would like to centralize validation logic in #ensure but I am not sure how to do it.
This is an example from documentation:
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
export class Person {
static inject() { return [Validation];}
//I want to inject validation logic here instead of using function(it){...}
#ensure(function(it){ it.isNotEmpty().hasLengthBetween(3,10) })
firstName = 'John';
constructor(validation) {
this.validation = validation.on(this);
}
}
And I would to change the code above to like below:
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import {UserValidation} from 'user/userValidation'; //custom validation logic here
export class Person {
static inject() { return [Validation];}
//can I do something like this instead of using function(it){...}?
#ensure(UserValidation.firstName)
firstName = 'John';
constructor(validation) {
this.validation = validation.on(this);
}
}
If we need to collect first name only on 1 page then we don't have to do this at all but since we might have to have user to enter their first name on multiple different pages, we would like to centralize the validation logic somewhere so that we don't have to copy & paste it everywhere. We don't want to create "first name component" either because UI will be different on each page, so we just want to reuse the validation logic.
UPDATE:
I asked this question in Aurelia discussion and was asked to try the following.
//userValidation.js
export function firstName(it){ it.isNotEmpty().hasLengthBetween(3,10)};
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import * as userValidation from 'user/userValidation';
export class Person {
static inject() { return [Validation];}
#ensure(userValidation.firstName)
firstName = 'John';
constructor(validation) {
this.validation = validation.on(this);
}
}
But I am getting this error: Unhandled promise rejection Error: Error instantiating Person. Any idea how I can fix this?
Actually, the following code worked!
//userValidation.js
export function firstName(it){ it.isNotEmpty().hasLengthBetween(3,10)};
//person.js
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import * as userValidation from 'user/userValidation';
export class Person {
static inject() { return [Validation];}
#ensure(userValidation.firstName)
firstName = 'John';
constructor(validation) {
this.validation = validation.on(this);
}
}

Can typescript external modules have circular dependencies?

It looks like this is not allowed. requireJS is throwing an error on the following (this post is different as it was resolved with internal modules):
element.ts:
import runProperties = require('./run-properties');
export class Element {
public static factory (element : IElement) : Element {
switch (element.type) {
case TYPE.RUN_PROPERTIES :
return new runProperties.RunProperties().deserialize(<runProperties.IRunProperties>element);
}
return null;
}
}
run-properties.ts:
import element = require('./element');
export class RunProperties extends element.Element implements IRunProperties {
}
No, modules can't have circular dependencies unless they are in the same file. Each file is being processed in sequence, synchronously, so the full file definition (including all of the exports for example) hasn't been completed when it goes to second file, which immediately tries to require/reference the first file, and so on.
Normally, you can break a circular dependency by introducing an interface or base class into a common definition file(s) (basically interfaces only) and having the other files use that as a common "interface" rather than directly referencing the classes. This is a typical pattern in many platforms.
I have same issue, I was able to fix it by creating factory class that allows registration of child classes and used Generics for instantiation.
Reference: https://www.typescriptlang.org/docs/handbook/generics.html#using-class-types-in-generics
See sample code below:
Base Class (abstract.control.ts)
export type AbstracControlOptions = {
key?:string;
}
export abstract class AbstractControl {
key:string;
constructor(options:AbstracControlOptions){
this.key = options.key;
}
}
Parent Class (container.ts)
import { AbstractControl, AbstracControlOptions } from './abstract.control';
import { Factory } from './factory';
export { AbstracControlOptions };
export abstract class Container extends AbstractControl {
children: AbstractControl[] = [];
constructor(options: AbstracControlOptions) {
super(options);
}
addChild(options: { type: string }) {
var Control:any = Factory.ControlMap[options.type];
if (Control) {
this.children.push(Factory.create(Control, options));
}
}
}
I don't have to import the child classes any more, because I'm using factory.ts to instantiate the child classes.
Factory Class(factory.ts)
import {AbstractControl, AbstracControlOptions} from './abstract.control';
type ControlMap<T extends AbstractControl> = {
[type:string]:T
};
export class Factory{
static ControlMap: ControlMap<any> = {};
static create<T extends AbstractControl>(c: { new ({}): T; }, options: AbstracControlOptions): T {
return new c(options);
}
}
Although class constructor seems to be called at c: { new ({}): T } but it does not actually calls it. But gets the reference to the constructor via new operator. The parameter {} to the constructor in my case is required because the base class AbstractControl requires it.
(1) Child Class(layout.ts)
import { Factory } from './factory';
import { Container, AbstracControlOptions } from './container';
export type LayoutlOptions = AbstracControlOptions & {
type:"layout";
}
export class Layout extends Container {
type: string = "layout";
constructor(options:LayoutlOptions) {
super(options);
}
}
Factory.ControlMap["layout"] = Layout;
(2) Child Class(repeater.ts)
import { Factory } from './factory'
import { Container, AbstracControlOptions } from './container';
export type RepeaterOptions = AbstracControlOptions & {
type: "repeater";
}
export class Repeater extends Container {
type: string = "repeater";
constructor(options:RepeaterOptions) {
super(options);
}
}
Factory.ControlMap["repeater"] = Repeater;