Creating the Context

Introduction

Let's now get hands on creating different beans through the context.

The principle is always the same: use DiContextFactory.createContext to pass the bean names with their bean definitions.

We will now show all the different use cases starting from the simple cases to the complex ones.

Creating beans

Adding values to the context

The simplest bean to create is a value:

const diContext = DiContextFactory.createContext({
    baseUrl: 'localhost' // this is a value
});

Adding values let's you access them directly

console.log(diContext.baseUrl); //This will printout 'localhost'

Adding hints to the context

The real value of the dependency injection is to get it to create complex objects for you.

This is the case for hints, (functions or class definitions)

Parameterless hints

When you need to add hints, which class constructor or function are parameterless, you can do this by passing the class or the function reference directly.

class MyAmazingClass {
    saySomething (){ return 'something'}
}

const diContext = DiContextFactory.createContext({
    myAmazingObject: MyAmazingClass, // this is class hint
});
console.log(
    diContext.myAmazingObject.saySomething()
); //This will print 'something'

Internal dependencies

Now that we have seen the simplest case of parameterless hints, let's see what would happen if we needed to specify an internal dependency.

Let's also show something closer to a real world case, let's imagine that the strings to be used for MyAmazingClass could be configured from the outside as you might need to if your applications supports many locales.

class MyAmazingClass {
    constructor (somethingStr){
        this.somethingStr = somethingStr;
    }
    
    saySomething (){ return this.somethingStr}
}

const diContext = DiContextFactory.createContext({
    myAmazingObject: MyAmazingClass, // this is class hint
},{
    somethingStr: 'something'
);

Function hints

The same principle that applies to classes can also apply to functions.

Is your choice when hinting if you want to use a full fledge class or a function that returns something.

We can see this in the following example

function rollDice () {
    return Math.floor(Math.random() * 6) + 1  
}

const diContext = DiContextFactory.createContext({
    randomRoll: rollDice, // this is function hint
})

And also we can see how we can use function hints and internal dependencies.

function rollDice (maxValue) {
    return Math.floor(Math.random() * maxValue) + 1  
}

const diContext = DiContextFactory.createContext({
    randomRoll: rollDice, // this is function hint
}, {
    maxValue: 6
})

Functions/classes as values

Because functions and class are always assumed to be hints, you will find that it might now work as you expect if you need to have a dependency to be resolved to a function or a class...

For instance:

function sayHello (){
    return 'hello'
}
const diContext = DiContextFactory.createContext({
    sayHello
});

The bean definition in the example above 'sayHello' is considered to be a hint (is a function), so if you access it through the context, the actual result would be the string 'hello'

console.log(diContext.sayHello); //This will printout 'hello'
console.log(diContext.sayHello()); //This will throw an error

If you wanted the function to be used as a value, and not as a hint, you will have to wrap it into an additional function.

This will trick the framework in resolving it to your original function, for instance to receive as a bean the function sayHello, instead of its returning value:

function sayHello (){
    return 'hello'
}

const diContext = DiContextFactory.createContext({
    sayHello: ()=>sayHello
});

console.log(diContext.sayHello()); //This will print helllo

As of v1.0 we are hoping to provide with you with a simpler mechanism to pass functions and class as hints in one of our first next releases.

Combining Hints and Values

The real power from dependency injection is that it lets you combine any level of hints and/or values.

class StockService{
    constructor (stockEndpoint){
         this.stockEndpoint = stockEndpoint
    }
}

class Endpoint {
    constructor (baseUrl){
        this.baseUrl = baseUrl
    }
}

const diContext = DiContextFactory.createContext({
    stockService: StockService, // this is the hint to create a StockService
    stockEndpoint: Endpoint, // we need to populate the value for the baseUrl
},{
    baseUrl: 'localhost'
});

As you can see in the example above, we have:

  • baseUrl: An aux bean definition used to initialise an Endpoint

  • stockEndpoint: A hint for the class Endpoint. Note that the constructor receives a parameter named 'baseUrl', this matches the aux bean definition, which means that it will be injected into the stockEndpoint bean.

  • stockService. A hint for the class StockService, the bean created in this context will receive injected through the constructor the stockEndpoint.

Dynamic context

A side effect of working with plain objects to store the context, is that as with as any other normal object, you can use functions to generate the context, or that you can change the context after it has been created.

You will see in the next section how this combined with the other features from dependency injection will let you for example write simpler integration tests.

We think that having a dynamic context is key to fully take advantage of the dependency injection

As of v1.0, we don't have any out of the box mechanism to help you create dynamic context, but we are hoping to change this in the near future.

Last updated