To define a factory, service, controller or a directive, use the following format:
exports.inject = function(app) {
app.factory('MyFactory', exports.factory);
return exports.factory;
}
exports.factory = function() {
return {doStuff: function() {}};
};
Then to use this factory as a dependency to another browserify module (e.g. a controller):
exports.inject = function(app) {
require('./path/to/my-factory').inject(app);
app.controller('MyController', exports.controller);
return exports.controller
};
exports.controller = function MyController$ng($scope, MyFactory) {
MyFactory.doStuff();
};
Then you can specify this controller from routeProvider:
$routeProvider.when('/myroute', {
templateUrl: '/site/files/index.html',
controller: require('./path/to/MyController').inject(app)
});
Before minifying, use the ngbmin preprocessor: it transforms all function expressions that end in $ng to the array injection syntax. Unlike ngmin, ngbmin will always do the right thing without trying to solve the halting problem.
Normally angular demands that you put all dependencies in the place where you define the module. This means that you have two options:
-
define all modules in the main module then manually check if they're still needed every time you remove a dependency anywhere, or
-
put everything in a separate module, which means duplicate dependency strings for every single entity.
But with this approach, dependencies are handled on-the-spot where they are needed, unlike raw angular. This means that if you remove all controllers (or perhaps routes) that depend on 'fooService', 'fooService' will also be removed from the resulting minified code, automatically.
It's testable, because you can just inject the exported controller or service manually in tests, or inject everything but replace certain things with others.
There is some boilerplate code involved.
The modules still name themselves instead of the naming happening at import.
Considered, but didn't implement:
module.exports = ngb.controller(function $ngb(inject) {
var $scope = inject('$scope');
var service = inject('./path/to/myService');
var regularModule = require('regular-module');
});
becomes
module.exports = angular
.module(__filename, [require.resolve('./path/to/myService')])
.controller(__filename, ['inject', function(inject) {
inject = inject(require);
var scope = inject('$scope');
var service = inject('./path/to/myService');
var regularModule = require('regular-module');
});
inject
is a helper service containing a path function that returns an inject function that either injects by path, or failing that, attempts to inject by name:
function inject(require) {
return function inject(name) {
try { return $injector.get(require.resolve(name)); }
catch (e) { return $injector.get(name); }
}
}
Why was this approach rejected?
- Complex
- The code will be coupled with the precompiler, which means it will be harder to backpedal if it turns out to be a bad idea.
- Most importantly, angular also has places where injection is allowed but defining new modules is forbidden. Didn't know how to handle those in terms of finding the responsible module and adding the necessary dependencies there.
Maybe this is for an old version of Angular, or there is something that I'm missing. Right now I use Angular with browserify by just doing:
You can use a controller name instead of passing an instance.
Then in the module configuration:
On the
controllers
directory I put aindex.js
file that does:On
ControllerName.js
, I use the$inject
function property so Angular DI works:This approach is working for me on Angular 1.2.0rc2