A familiar application context and dependency injection package for JavaScript, supporting simple singleton and prototype component factory definitions, with a choice of manual or auto wiring (injection) of property references and config placeholders.
The module is also able to be used directly in the browser, in combination with the logger and config modules. You can either import the ApplicationContext globally as an IIFE (Immediately Invoked Function Expression), as follows:
<script src="https://cdn.jsdelivr.net/npm/@alt-javascript/cdi/dist/alt-javascript-applicationcontext-iife.js"></script>
<script>
import { SimpleClass } from'./index.js';
const applicationContext = new ApplicationContext(SimpleClass);
</script>
Or import the ES6 module bundle from a module, as follows:
import { ApplicationContext } from 'https://cdn.jsdelivr.net/npm/@alt-javascript/cdi/dist/alt-javascript-cdi-esm.js'
import { SimpleClass } from'./index.js';
const applicationContext = new ApplicationContext(SimpleClass);
To configure a simple singleton service object, simply instantiate an ApplicationContext
and pass it
the class definition. The default scope is singleton
, and the component name defaults to lowerCamelCase
of the class name.
import { ApplicationContext } from '@alt-javascript/cdi';
import { SimpleClass } from'./index.js';
const applicationContext = new ApplicationContext(SimpleClass);
applicationContext.start();
applicationContext.get('simpleClass');
Simple types, objects and functions can be registered as components.
import { ApplicationContext } from '@alt-javascript/cdi';
import { SimpleClass } from './index.js';
const applicationContext = new ApplicationContext([
{name: 'someData', attr:'aValue', behave: () => {}},
{Reference : (what) => { console.log (`Hello ${what}`)},
name : 'aFunc'}]);
applicationContext.start();
applicationContext.get('someData').behave();
applicationContext.get('aFunc')('world!');
Singletons can also be defined with the common alias names Service
,Component
and Singleton
import { ApplicationContext, Singleton, Service, Component } from '@alt-javascript/cdi';
const { SimpleSingleton, SimpleService, SimpleSingleton } from './index.js';
const context = new Context([
new Singleton(SimpleSingleton),
new Service(SimpleService),
new Component(SimpleSingleton)]);
const applicationContext = new ApplicationContext([context]);
applicationContext.start();
applicationContext.get('simpleSingleton');
applicationContext.get('simpleService');
applicationContext.get('simpleComponent');
Prototype, or transient scoped objects can be defined with Prototype definition, or Transient definition. These objects are created each time they are requested from the context, or wired by the context lifecycle.
import { ApplicationContext, Prototype, Transient } from '@alt-javascript/cdi';
import { SimpleClass, SimpleTransient } from './index.js';
const applicationContext = new ApplicationContext(new Prototype(SimpleClass));
const applicationContext = new ApplicationContext(new Transient(SimpleTransient));
applicationContext.start();
applicationContext.get('simpleClass');
Use the Component class to declare the full explicit definition of a component, allow full control.
import { ApplicationContext, Component } from '@alt-javascript/cdi';
import { SimpleClass, SimpleTransient } from './index.js';
const applicationContext = new ApplicationContext(
new Component({
Reference : SimpleClass,
name : 'useAnExplicitName',
qualifier : '@my-scope/SimpleClass',
scope : Scopes.SERVICE,
}));
applicationContext.start();
applicationContext.get('useAnExplicitName');
A component can be created by referencing a factory function directly.
import { ApplicationContext, Component } from '@alt-javascript/cdi';
import { MyClass } from './index.js';
const applicationContext = new ApplicationContext(
new Component({
factory : MyClass.someStaticFunction(),
name : 'fromAFactory',
qualifier : '@my-scope/SimpleClass',
scope : Scopes.SERVICE,
}));
applicationContext.start();
applicationContext.get('useAnExplicitName');
Component properties are autowired by name, by default. In the example classB
and classC
will be autowired,
but classD
, which is non-null will be let alone. The attribute
will remain null if not found in the application
context.
export default class ClassA {
constructor() {
this.classB = null;
this.classC = 'autowired';
this.classD = new new ClassD();
this.attribute = null;
}
};
Configuration values that are booted with @alt-javascript/boot
are injected with the familiar placeholder syntax.
export default class ClassA {
constructor() {
this.attribute = '${get.this.from.config}';
}
};
Properties can be injected, directly from functions, or from references to functions on other components using
the explicit Property
declaration class.
const context = new Context([
{
name: 'singletonFactory',
generator: (param) => ({ name: 'simplePrototype', attr: param }),
},
new Service({
Reference: 'MyServiceService',
properties: [new Property({
name:'myServiceProperty',
factory: 'singletonFactory',
factoryFunction: 'generator',
factoryArgs: 'one'})]
})]);
const applicationContext = new ApplicationContext([context]);
applicationContext.start();
const myServiceService = applicationContext.get('myServiceService');
assert.exists(myServiceService, 'myServiceService exists');
assert.equal(myServiceService.myServiceProperty.attr, 'one', 'myServiceService.myServiceProperty.attr == one');
Where the context of the target component being wired is required, a wireFactory
can be declared on a prototype
and the target Component
instance is passed as an argument by default. The ApplicationContext
declares a
logger
prototype with the loggerFactory
component as the wireFactory
, allowing the component qualifier to be
used as the logger category (true).
The ApplicationContext is designed to play nicely with other projects in the @alt-javascript
scope, so the
booted config
, loggerFactory
, logger
, loggerCategoryCache
are available.
Any component declared with a logger
and qualifier
properties will be injected with an appropriate logger,
as if by magic.
May be freely distributed under the MIT license.
Copyright (c) 2021 Craig Parravicini