0.1.0 • Published 3 years ago

mithril-di v0.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Mithril DI

This library implements a dependency injection mechanism for mithril js.

How to

es6 import

import { DI } from 'mithril-di'

commonjs import

const { DI } = require('mithril-di')

Basics

Use the DI function to wrap your component definition whenever a component should provide or inject data.

To make a component a provider of something, pass a providers array as part of the options argument (1st argument). The providers syntax is the same as

const Parent = DI(
  // DI options
  {
    providers: [{ provide: 'greetings', useValue: 'Hello World' }],
  },
  // component definition
  () => {
    return {
      view: () => m(Child),
    }
  },
)

A component that is wrapped by the DI function will be given an additional injector attribute which then can be used to access provided data.

Closure Components

The injector attribute can be consumed in all lifecycle methods. However, for closure components one would most likely inject at the beginning of closure.

const Child = DI(({ attrs }) => {
  const text = attrs.injector.get('greetings')
  return {
    view: () => text, // -> Hello World
  }
})

POJO Components

POJO components dont have a constructor, so the next best place to use the injector would be the oninit lifecycle method

const Child = DI({
  oninit: ({ attrs, state }) => {
    state.text = attrs.injector.get('greetings')
  },
  view: ({ state }) => {
    return state.text
  },
})

Class Components

Class components may also be wrapped with the DI function. It looks a bit strange, but the DI behavior is consistent with the previous examples: injector is available in all lifecycle methods as well as in the constructor

const Child = DI(
  class {
    text: string
    constructor(n) {
      this.text = n.attrs.injector.get('greetings')
    }
    view() {
      return text
    }
  },
)

Class Components (decorators)

The DI function also acts as a class decorator. Together with the Inject decorator this results in a cleaner syntax.

The main draw back is that the injection happens right before the view method. You can not use the injected data inside oninit but only inside view and oncreate.

import { DI, Inject } from 'mithril-di'

@DI()
class Child {
  @Inject('greetings')
  private text

  oninit() {
    console.log(this.text) // -> undefined
  }

  oncreate() {
    console.log(this.text) // -> "Hello World"
  }

  view() {
    console.log(this.text) // -> "Hello World"
    return this.text
  }
}

Providers

class provider

class MyService {
  constructor(injector: Injector) {}
}

DI(
  {
    providers: [
      {
        provide: MyService,
      },
    ],
  },
  (injector) => {
    injector.get(MyService)
    return {
      view: () => null,
    }
  },
)

ClassProvider

class MyService {
  //
}

class AnotherService {
  constructor(injector: Injector) {}
}

DI(
  {
    providers: [
      {
        provide: MyService,
        useClass: AnotherService,
      },
    ],
  },
  (injector) => {
    injector.get(MyService) // -> instance of AnotherService
    return {
      view: () => null,
    }
  },
)

value provider

class MyService {
  //
}

DI(
  {
    providers: [
      {
        provide: MyService,
        useValue: new MyService(),
      },
    ],
  },
  (injector) => {
    injector.get(MyService) //
    return {
      view: () => null,
    }
  },
)

provider factory

class MyService {
  //
}

DI(
  {
    providers: [
      {
        provide: MyService,
        useFactory: (injector) => new MyService(injector),
      },
    ],
  },
  (injector) => {
    injector.get(MyService) //
    return {
      view: () => null,
    }
  },
)

provider aliasing

class MyService1 {}

class MyService2 {}

DI(
  {
    providers: [
      { provide: MyService1 },
      {
        provide: MyService2,
        useExisting: MyService1,
      },
    ],
  },
  (injector) => {
    injector.get(MyService2) // -> instance of MyService1
    return {
      view: () => null,
    }
  },
)