easy-injectionjs

A package that provides dependency injection using common design patterns as stereotypes. It easily injects depedencies in runtime and supports inheritance:

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
easy-injectionjs
000.1.424 years ago4 years agoMinified + gzip package size for easy-injectionjs in KB

Readme

easy-injectionJS
A package that provides dependency injection using common design patterns as stereotypes. It easily injects depedencies in runtime and supports inheritance:
There's no additional dependencies or containers needed to use the package. You can simply use it as follows:
npm i -S easy-injectionjs
Or
yarn add easy-injectionjs
Dependencies without inheritance:
import { Easy, EasyPrototype, EasySingleton } from 'easy-injectionjs';

// Creates a singleton instance of Movie class in runtime
@EasySingleton()
class Movie {
  constructor (private name: string, private ratings: number) {
    // Lets say that they just release the movie :D
    this.name = 'Black Panther';
    this.ratings = 9.0; // I really like that movie
  }

  public getName(): string {
    return this.name;
  }

  public getRatings(): number {
    return this.ratings;
  }

  public setName(v: string) {
    this.name = v;
  }

  public setRatings(v: number) {
    this.ratings = v;
  }
}

// Creates a multiple instances as needed in runtime
@EasyPrototype()
class Actor {
  @Easy()
  private movie: Movie;
  private name: string;

  constructor (name: string) {
    this.name = name;
  }

  public getMovieName(): string {
    return this.movie.getName();
  }

  public setMovieName(v: string) {
    this.movie.setName(v);
  }
}

let T_Challa = new Actor('Chadwick Boseman');
console.log(T_Challa.getMovieName());// Output is Black Panther
T_Challa.setMovieName('Black Panther II'); // Hopefully
console.log(T_Challa.getMovieName()); // Output is Black Panther II
let Erik_Killmonger = new Actor('Michael B Jordan');
console.log(Erik_Killmonger.getMovieName()) // // Output is Black Panther II
If the movie had the annotaion of EasyPrototype instead, ErikKillmonger.getMovieName() will return Black Panther istead of Black Panther 2.
Interfaces and named dependencies example:
import { Easy, EasyPrototype, EasySingleton } from 'easy-injectionjs';

interface Show {
  getName(): string;
  getRatings(): number;
  setName(v: string);
  setRatings(v: number);
}

// Creates a singleton instance of Movie class in runtime
@EasySingleton('Movie')
class Movie implements Show {
  constructor (private name: string, private ratings: number) {
    // Lets say that they just release the movie :D
    this.name = 'Black Panther';
    this.ratings = 9.0; // I really like that movie
  }

  public getName(): string {
    return this.name;
  }

  public getRatings(): number {
    return this.ratings;
  }

  public setName(v: string) {
    this.name = v;
  }

  public setRatings(v: number) {
    this.ratings = v;
  }
}

// Creates a multiple instances as needed in runtime
@EasyPrototype()
class Actor {
  @Easy('Movie')
  private movie: Show;
  private name: string;

  constructor (name: string) {
    this.name = name;
  }

  public getMovieName(): string {
    return this.movie.getName();
  }

  public setMovieName(v: string) {
    this.movie.setName(v);
  }
}

let T_Challa = new Actor('Chadwick Boseman');
console.log(T_Challa.getMovieName());// Output is Black Panther
T_Challa.setMovieName('Black Panther II'); // Hopefully
console.log(T_Challa.getMovieName()); // Output is Black Panther II
let Erik_Killmonger = new Actor('Michael B Jordan');
console.log(Erik_Killmonger.getMovieName()) // // Output is Black Panther II
The package support inheritance even with abstract classes using the @EasyFactory decorator:
Example:
import { Easy, EasyFactory, EasyPrototype, EasySingleton } from 'easy-injectionjs';

@EasyFactory()
abstract class Show {
  public abstract getName(): string;
  public abstract getRatings(): number;
  public abstract setName(v: string);
  public abstract setRatings(v: number);
}

// Creates a singleton instance of Movie class in runtime
@EasySingleton()
class Movie extends Show {
  constructor (private name: string, private ratings: number) {
    super();
    // Lets say that they just release the movie :D
    this.name = 'Black Panther';
    this.ratings = 9.0; // I really like that movie
  }

  public getName(): string {
    return this.name;
  }

  public getRatings(): number {
    return this.ratings;
  }

  public setName(v: string) {
    this.name = v;
  }

  public setRatings(v: number) {
    this.ratings = v;
  }
}

// Creates a multiple instances as needed in runtime
@EasyPrototype()
class Actor {
  @Easy()
  private movie: Show;
  private name: string;

  constructor (name: string) {
    this.name = name;
  }

  public getMovieName(): string {
    return this.movie.getName();
  }

  public setMovieName(v: string) {
    this.movie.setName(v);
  }
}

let T_Challa = new Actor('Chadwick Boseman');
console.log(T_Challa.getMovieName());// Output is Black Panther
T_Challa.setMovieName('Black Panther II'); // Hopefully
console.log(T_Challa.getMovieName()); // Output is Black Panther II
let Erik_Killmonger = new Actor('Michael B Jordan');
console.log(Erik_Killmonger.getMovieName()) // // Output is Black Panther II
Both Iterfaces and abstract classes inheritance support naming to differentiate between the children dependencies and the names have to go into the @Easy decorator and have to match the names in the @EasyPrototype and @EasySingleton decorators. You can even name the @EasyFactory decorator if u have many abstract classes extending each others.
You can even not name anything and let the package figure out which dependencies as needed:
import { Easy, EasyFactory, EasyPrototype, EasySingleton } from 'easy-injectionjs';

@EasyFactory()
abstract class Person {
  abstract getName();
  abstract setName(v: string);
}

// @EasyObservable()
@EasySingleton()
class Somebody extends Person{
  // @Easy()
  constructor (private name: string) {
    super()
    this.name = 'Sal';
  }

  public getName() {
    return this.name;
  }
  public setName(v: string) {
    this.name = v;
  }
}

@EasyPrototype()
class Nobody extends Person{
  @Easy()
  somebody: Person;
  constructor () {
    super()
  }
  
  public getName() {
    return this.somebody.getName();
  }

  public setName(v: string) {
    this.somebody.setName(v);
  }
}

@EasyPrototype()
class Data {
  @Easy()
  somebody: Person;
  name: string;

  change(v: string) {
    this.somebody.setName(v);
  }

  getName(): string {
    return this.somebody.getName();
  }
}

let n = new Nobody();
console.log(n.getName()) // Prints Sal
n.setName('awesome');
console.log(n.getName())  // Prints awesome
let d = new Data()
console.log(d.getName())  // Prints awesome
d.change('Gelba')
console.log(n.getName())  // Prints Gelba
d.change('kaa')
console.log(n.getName())  // Prints Kaa

The "is" Keyword:

The "is" function is used to retrieve dependencies anywhere in the program. It can also be usint with constructor injection:

Example:

import { EasySingleton,
         EasyPrototype,
         is } import 'easy-injectionjs';

@EasySingleton()
class Somebody {
  // @Easy()
  constructor (private name: string) {
    this.name = 'Vladi';
  }

  public getName() {
    return this.name;
  }
  public setName(v: string) {
    this.name = v;
  }
}

@EasyPrototype()
class Awesome {
  private _name: string;
  constructor (someone: Somebody = is(Somebody)) {
    someone.setName('Sal')
    this._name = someone.getName();
    console.log(this._name);
  }
}

is(Awesome) // call returns an instance of Awesome

console.log(is(Somebody).getName()) // returns Sal since it is a singleton :D

The "Easily" Method:

The "Easily" is ideally used to define static constants or simple objects like:
import { Easily } from 'easy-injectionjs';

const config = {
  author: 'Sal'
}
Easily('config', config);

It requires a name to be injected. PS it can work for npm packages also, if imported like this: import as smth from 'package'
@EasyPrototype()
class Nobody {
  @Easy('config')
  config: Object;
  constructor () {
    super()
  }
  
  public getName() {
    return this.somebody.getName();
  }
 
  public setName(v: string) {
    this.somebody.setName(v);
  }
}

A bit complicated example:
import {Easy, EasyPrototype, EasySingleton, EasyFactory, Easily, is} from 'easy-injectionjs';

Easily('config', {
  feeling: 'hungry'
})

@EasyFactory()
abstract class Person {
  abstract getName();
  abstract setName(v: string);
}

@EasySingleton()
class Somebody extends Person{
  // @Easy()
  constructor (private name: string) {
    super()
    this.name = 'Sal';
  }
 
  public getName() {
    return this.name;
  }
  public setName(v: string) {
    this.name = v;
  }
}
 
@EasyPrototype()
class Nobody extends Person{
  @Easy()
  somebody: Person;
  @Easy('config')
  config: Object;
  constructor () {
    super()
  }
  
  public getName() {
    return this.somebody.getName();
  }
 
  public setName(v: string) {
    this.somebody.setName(v);
  }
}

@EasyFactory()
abstract class Show {
  public abstract getName(): string;
  public abstract getRatings(): number;
  public abstract setName(v: string);
  public abstract setRatings(v: number);
}
 
// Creates a singleton instance of Movie class in runtime
@EasySingleton()
class Movie extends Show {
  constructor (private name: string, private ratings: number) {
    super();
    // Lets say that they just release the movie :D
    this.name = 'Black Panther';
    this.ratings = 9.0; // I really like that movie
  }
 
  public getName(): string {
    return this.name;
  }
 
  public getRatings(): number {
    return this.ratings;
  }
 
  public setName(v: string) {
    this.name = v;
  }
 
  public setRatings(v: number) {
    this.ratings = v;
  }
}
 
@EasySingleton()
class Data {
  @Easy()
  somebody: Person;
  name: string;
  @Easy()
  movie: Movie;
  change(v: string) {
    this.somebody.setName(v);
  }
 
  getName(): string {
    return this.somebody.getName();
  }
}
 
let n = new Nobody();
console.log(n)
console.log(n.getName()) // Prints Sal
n.setName('awesome');
console.log(n.getName())  // Prints awesome
let d = new Data()
console.log(d)
console.log(d.getName())  // Prints awesome
d.change('Gelba')
console.log(n.getName())  // Prints Gelba
d.change('kaa')
console.log(n.getName())
Even method injection is very easy using both "Easily", the decorators and "is". That makes dynamic injection very easy.
import { Easily } from 'easy-injectionjs';

Easily('Data', {
  name: 'Sal'
});
And somewhere in the program in another file:
import { is, Easily } from 'easy-injectionjs';

function modifyData(data: any = is('Data')) {
  data.name = 'I am Batman';
  // update it
  Easily('Data',data);
}

And maybe another class requires "Data":
import { EasyPrototype, Easy } from 'easy-injectionjs';
// why not :D
@EasyPrototype()
class Mule {
  @Easy('Data')
  private data:any;
}
If modifyData was called before Mule was used, the data name inside the property data of mule will change in runtime.