Dependency injection for JavaScript.
Working across multiple stacks, I've found dependency injection a blessing in strongly typed languages like Java. This module provides just that. A simple and clean dependency framework for people to use with JavaScript. There are basic two things to injection. The where and the how. Where specifies what function to do the injection in and how specifies how to inject something in. As of now, the library supports two kinds of injections. Injection using a static variable and injection using a factory function. Using a static variable, the framework will inject the value of required variable straightaway. However, when using factory functions, injection will resolve the factory function and its dependencies first, execute the factory function and then inject the return value from that factory function. The framework takes care of this out of the box so you don't have to worry.
Using the native mode is the quickest way to get started.
// Require function definitions
var Injector = require("injector").Injector;
var Injectable = require("injector").Injectable;
var InjectorScope = require("injector").InjectorScope;
// create your injector
var myInjector = new Injector();
// bind an injectable variable
myInjector.bind(Injectable.newVariable("name", "Manthan"));
// or bind a factory function
var getDate = function() {
return new Date();
};
myInjector.bind(Injectable.newFactory("date", getDate));
// enable native injection
Injector.enableNativeInjection(myInjector);
// define your function that you want to use injection on
var printName = function(name) {
console.log("Hello %s!", name);
};
// execute without any parameters
printName.ix();
Setup is quick and simple. To install the module in your project, do:
npm install
Once installed, three function definitions will have to be imported.
var Injector = require("injector").Injector;
var Injectable = require("injector").Injectable;
var InjectorScope = require("injector").InjectorScope;
In order to use the injector, you'll have to provide it with some injectables. An injectable is an object that tells the framework how something can be injected. To bind a variable, you'll need to create a injectable first. Injectable defines what it is that you are asking the framework to inject.
var myInjectable = Injectable.newVariable("name", "Manthan");
Once you have an injectable, create the injector instance.
var myInjector = new Injector();
And then bind the injectable to the injector.
myInjector.bind(myInjectable);
You can also bind injectables to a InjectorScope instance which you can then pass into an injector as it's initial scope.
var myScope = new InjectorScope();
myScope.add(myInjectable);
myInjector = new Injector(myScope);
At any given time, an injector has at most two scopes. One is the scope that it has when it is first created. This is the root scope of the injector. The second scope is optional and can be passed in as part of the inject
or injectAndExecute
functions on the injector itself. This second scope overrides values from the first scope. This means that if secondScope has {name: "Dave"}
and the root scope has {name: "Manthan"}
, upon injection name will be injected as Dave
.
Injector will need a function to inject its injectables in. This can be done in two ways. One is to use the normal inject where it will inject the variables and return a prepared function. A prepared function is a function that already has everything injected in and is ready to be executed.
var printName = function(name) {
console.log("Hello %s!", name);
};
var injectedPrintName = myInjector.inject(printName);
injectedPrintName();
Here injectedPrintName()
is a prepared function.
Another way is to let injector execute the function for you. This can be done like so:
myInjector.injectAndExecute(printName);
You can also pass in a child scope while calling inject
or injectAndExecute
function(s). This for those special cases where something that you're trying to inject isn't constant across all things and you want to override something just this once. Values in root scope are never overwritten. They are simply less prefered when a child scope is provided.
var childScope = new InjectorScope();
childScope.add(new Injectable("name", "Dave"));
myInjector.injectAndExecute(printName, childScope);
Sometimes you may want to inject a value that is generated by a calling a function. This can be achieved by creating a factory injectable, binding it to root or current scope and then executing a function that needs the value.
var getDate = function() {
return new Date();
};
myInjector.bind(Injectable.newFactory("date", getDate));
var printDate = function(date) {
console.log("Date is %s", date);
};
myInjector.injectAndExecute(printDate);
In this example, getDate
is a factory function which we've bound to the rootScope
of myInjector
. Note that the injectable that we've created is bound to simple date
. Further on, this date
variable is required by the printDate
function. Upon injection, the injector sees that the printDate
requires date
variable. It also sees that this variable can be obtained by calling the getDate
function. Thus, it executes getDate
function, retrieves its value and then injects its value in the printDate
function.
More often than not, the factory function may require other variables as parameters in order for it to generate its value. As long as these parameters are in the rootScope or current scope, the injection framework will suffice all the dependencies to nth degree. Here's a example.
var getCurrentDate = function() {
return new Date();
};
var getProfile = function(date) {
return {
name: "Manthan",
generatedOn: date
};
};
var printProfile = function(profile){
console.log("Profile for: %s. Generated on %s", profile.name, profile.generatedOn);
};
var injector = new Injector();
injector.bind(Injectable.newFactory("date", getCurrentDate));
injector.bind(Injectable.newFactory("profile", getProfile));
injector.injectAndExecute(printProfile);
We've got two factory functions in the above example. One is the getCurrentDate
function that returns the current date and does not require anything in its function parameters. The second factory function is the getProfile
function which requires date
in its parameters and returns a profile object.
Using the injector we've bound the two functions getCurrentDate
as date
and getProfile
as profile
variables. Then using the injectAndExecute
function, we're executing the printProfile
function. Injector will resolve the dependency on profile
that this function has by calling the getProfile
function but before it can do that it needs to resolve the dependency that getProfile
has on date
by calling the getCurrentDate
function.
Defines a new Injector
instance with its root scope being initialScope
if provided.
Allows binding of an injectable to the rootScope
. Throws TypeError
when parameter injectable
is not of type Injectable
.
#####Parameter: injectable
Required. Must be of type Injectable
.
Returns a prepared function that has it's parameters injected in and is ready to be executed without any parameters.
Required. Must be a function.
Optional. If provided, must be of type InjectorScope
. Throws TypeError
otherwise.
Injects all required dependencies and executes provided function. Prefers child scope if provided as scope
.
Required. Must be a function.
Optional. If provided, must be of type InjectorScope
. Throws TypeError
otherwise.
Enables native injection for all functions at once for the specified injector
. Adds two methods i() and ix() to all native functions.
i(scope)
Returns prepared functions with everything injected in and ready to go. Accepts an optional child scope as scope
. scope
must be of type InjectorScope
.
ix(scope)
Injects required parameters and executes the function. Accepts an optional child scope as scope
. scope
must be of type InjectorScope
.
Required. Must be an injector.
Defines a new InjectorScope
instance.
Returns injectable bound to the scope by name name
.
Required. Must be a string.
Allows binding of an injectable to the scope instance. Throws TypeError
when parameter injectable
is not of type Injectable
.
#####Parameter: injectable
Required. Must be of type Injectable
.
Defines a new injectable instance.
Returns the name of the injectable. This is used to bind the injectable to a scope.
Returns the value of the injectable.
Returns whether or not the injectable is a factory.
Returns whether or not the injectable is a variable.
Returns a new injectable that is a static variable. Use this to create an injectable of type static variable.
Required. Name of the injectable. This is the name that is used to bind the injectable to a scope (if ever).
Required. Value of the injectable.
Returns a new injectable that is a factory. Use this to create an injectable of type factory.
Required. Name of the injectable. This is the name that is used to bind the injectable to a scope (if ever).
Required. Value of the injectable. Must be a function. Throws TypeError
if it isn't.