young-steveo / bottlejs

A powerful dependency injection micro container for JavaScript applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Suggestion] Full example with nested directories

juicetin opened this issue · comments

commented

Hi,

Currently trying to get started with this library - given that the library doesn't use config files, how does the recursive searching for all bottles from an entry-point index.js work? This seems to have been left out of the basic examples, as they appear to assume everything is in one single file.

Does the entry point still require the old-fashioned 'require' of every module with bottles inside, or did I completely miss something in the README? Thanks.

@Jyting You are correct; bottlejs is a DI container, but not a module loader.

There is no single correct way to load all of your modules into a container, but I can give you an examples.

Using the bottle.register method, and allowing your modules to self-describe their dependencies:

// /things/somehting.js
module.exports = function Something(A, B) { };
module.exports.$name = 'Something';
module.exports.$inject = ['A', 'B'];
// /things/a.js
module.exports = function A() { };
module.exports.$name = 'A';
// /things/b.js
module.exports = function B() { };
module.exports.$name = 'B';
// /index.js
const bottle = require('bottlejs').pop('yourcontainer');
const fs = require('fs');
fs.readdirSync('./things').forEach(filename => {
    bottle.register(require(filename));
});

Since the bottle is named, after you have loaded index.js you can get your bottle anywhere in your application using:

const bottle = require('bottlejs').pop('yourcontainer');
commented

Ah, gotcha - I guess an initial simple approach would as you described be to self-inject dependencies, then register everything by doing a recursive file search in index.js to hook everything up.

Makes sense - thanks for taking the time to write up the example!

commented

Actually, run into a bit of a snag - does the order of registering need to be in the same order as the dependencies themselves?

At the moment, I just do a recursive loop over all my files in the src folder, with each declaring its own name and dependencies via module.exports.$name and module.exports.$inject, where appropriate. However, I'm finding that due to, for example, the names of directories and subdirectories being loaded in alphabetical order, it goes:
load module A
load module B
load module C
when in fact, C requires B, and B requires A. It seems as though this scenario results in all the dependencies being undefined after looping over all the files and registering them with bottlejs.

In this case, do dependencies have to be required one by one and registered in the right order, or is it likely that I'm incorrectly doing the bottle registering?

Thanks again.

@Jyting The order of the registered dependencies does not matter. Here is a quick example that shows a service being registered before it's dependency: https://jsfiddle.net/yrz5017g/

The reason why this works is because bottle instantiates your service lazily. That is to say, until the property bottle.container.NeedsLogger is accessed for the first time, the service and any dependencies are not yet instantiated.

As soon as bottle.container.NeedsLogger is accessed for the first time, bottle looks at the dependencies it needs, creates those dependencies first (recursively for any subsequent dependencies up the chain) and injects them into NeedsLogger.

Where you might be running into trouble is trying to use one of your objects before you have registered one of it's dependencies.

You may want to set Bottle.config.strict = true. This will cause Bottle to throw an error if any dependencies are not defined before attempting to use a subordinate service.

commented

Thanks @young-steveo - I guessed that was the case and should have double checked everything before coming back to ask more silly questions.

Had a bit of inconsistent naming that was causing the error (the filename and the $name mismatched for one of my files).

Thanks for the tip re: config.strict = true though - that'll definitely help catch any future errors faster!

I think it would be useful to add a larger example to the readme, maybe similar to the one above, so you can see the intended usage. An example of replacing dependencies for testing would also be useful.