Angular's module system fails at tracking dependencies properly
Angular, being a framework with a lot of solutions to a problem, forces users to use modules - a very good thing. However, rather than letting users choose their module and package system (RequireJS or Browserify or Webpack for loaders and AMD vs CommonJS vs Harmony for module formats), it brings in it's own module format, without a loader.
Defining a module looks like this
var module = angular.module('fruit-stand', ['raspberries', 'strawberries', 'apples'])
// define what the module is or has by calling methods on it
it also includes a dependency injection component, because, uh, that's what they use in Java so JavaScript needs one too, because modules can't be scoped or replaced in a dynamic, interpretted language right? Nevermind. Well here's how it works:
angular.module('raspberries').constant([1, 2, 3])
angular.module('strawberries').factory(function() {
return // anything
})
angular.module('fruit-stand', ['raspberries', 'strawberries', 'apples']).controller(function(raspberries, strawberries, apples, $http, $scope) {
// extend $scope for what your controller needs
})
In .controller
, it will function scan it and see raspberries
will match the raspberries
module and inject what it exports. It turns out angular doesn't correlate or validate that your injections match your declared dependecies! so in a codebase I inherited, I discovered soemthing like this
// app.js
angular.module('app', ['some-common-directive', 'some-service', 'some-plugin']).config(['some-common-directive', 'some-service', function() {
}])
// some-common-directive.js
angular.module('some-common-directive').directive(function(some-plugin) { })
There was some unused dependency in app.js
(some-plugin
) so I removed it. Turns out it broke the app because some-common-directive
used it, but because you just throw all the scripts in the page, eventually someone else required it, so it could use it too without declaring it! Now I have no assurance that what my module declares as a depdencency is accurate at all - the main goal of a module system.
Compare this with the simple, straight-forward AMD, CJS, or Harmony formats:
// AMD
define(['raspberries', 'strawberries', 'apples'], function(raspberries, strawberries, apples) {
})
// CJS
var raspberries = require('raspberries')
// ES6 / Harmony
import raspberries
What you ask for is given to you in a local variable or parameter. Didn't ask for it? it's not there. TypeError and you blow up. Not surprizing as the answer is usually to just use a function.