Demystifying AngularJS' dependency injection
bdadam.com
bdadam.com
Angular is pretty cool overall, but I strongly dislike this part of the library. Inspecting the names of function parameters and matching them up with registered services is insane. In practice, you have to specify the dependencies manually anyhow:
foo.service('Bar', ['Baz', function(Baz) { ... }]);
This way, parameter names are irrelevant outside of the context of that function, like they should be. I wish this "feature" was dropped entirely. > In practice, you have to specify the dependencies
> manually anyhow:
In practice, everybody is using https://github.com/btford/ngmin and this is a non-issue.That said, the dependency injection system in angular.dart is a bit different, and we'll likely be learning and porting some of that back into AngularJS 2.0 over the coming months.
I understand the desire to build elegant abstractions via metaprogramming, but I think is an example of metaprogramming done poorly. This particular feature was a source of confusion for me and my coworkers when we first started learning to use Angular. I think that the explicit form is the only sane way, or at least one of the possible sane ways.
I'll $q.defer() to SICP to explain the principle that is being violated by the implicit form:
"One detail of a procedure's implementation that should not matter to the user of the procedure is the implementer's choice of names for the procedure's formal parameters. ...This principle -- that the meaning of a procedure should be independent of the parameter names used by its author -- seems on the surface to be self-evident, but its consequences are profound."
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-4.html#...
Pretend for a moment that Javascript had macros (sweet.js where are you?) and the Angular syntax was more explicit:
myModule.provider ProviderName
inject $scope, $document, SomeOtherDependency
{
your code here
}
The last thing you'd expect is a syntax where the injected dependency names were chosen by the implementor of the provider.Javascript's greatest weakness is that it's native syntax is poorly suited for creating language like extensions. The result is that things like modules, imports, etc. tend to be very verbose and noisy.
Well, sure. If and when JavaScript gains hygienic macros (via sweet.js or otherwise), I will like this sort of notation. However, the reality is that JavaScript doesn't have macros currently. Thus, I still dislike the magical dependency notation.
>The last thing you'd expect is a syntax where the injected dependency names were chosen by the implementor of the provider.
I don't see why this is bad. Several languages allow you to rename things in imported modules. The two that I can think of immediately are Python and Guile Scheme. Most of the time there is no reason to rename, but sometimes it is a good idea to rename for clarity or to avoid name clashes. This is something that you can already do via the explicit form of dependency injection.
Unfortunately, it isn't always so nice for the guy who has to read it three years later and figure out what's going on. Sometimes there is a fine line between useful tools that reduce boilerplate and leaky abstractions that hide important details you're often going to need anyway. I suspect some of the features in Angular today will prove to be on the wrong side of that line, though to be fair it's hardly the only JS framework from the recent crop where that concern applies.
However, for production apps, you're probably not using "magic" function annotation, at least not without the help of ngmin or a similar tool.
It's just a nice way to quickly write demonstrations and explain code. Arguably, the explicit annotations would not really inform you any better than implicit annotation, if you were unfamiliar with the framework.
So, I agree that explicit annotation is important, but I think the "magic" annotation also makes the framework a bit more accessible to new comers, and is also very handy for producing small demos, reproductions, or other small apps. The fact that we can offer the choice here is great.
A little bit of experience with the framework and all this stuff becomes pretty much second nature. I expect Ember or Knockout would be similar in that regard.
Basically, 1) understand the fundamentals of your framework. 2) use the right tools/methods for the right job. You can certainly write maintainable and understandable apps using all of the annotation styles.
At least, in my opinion.
Not really. Matching the identifier is simply an optional convenience for the developer, the "dependency injection" refers to the mechanism that allows you to configure, mock, or replace the "function parameter" before the framework injects it into scope.
I speak from bitter experience with a product that ended up being two months late because of the decision by a junior Seb to use Angular - I highly recommend avoiding it and learning JavaScript properly and then picking the best tools from NPM that you need.
I think your ad hominem and somewhat hyperbolic complaint would be more credible if you supplied more concrete examples of how Angular caused your product to be two months late. I'm not saying you're wrong. I'm just saying that nothing in your comment actually makes your point.
My team has made very effective use of Angular and far from making us late it has saved us considerable time.
We treat Angular not as an abstraction or framework but as a toolkit for doing the following:
* Extending the browser's built-in HTML and CSS with our own custom behaviour.
* Two way databinding between our application data and the DOM
* Architecting small testable / mockable components
For what it's worth, there are some angular-isms that I truly dislike. In particular, angular.module is a bug farm for early angular developers. Also, without ngmin, Angular's "provider" declarations is both noisy and error prone. The truth howerver, is that these are minor problems and are easily discovered and fixed immediately.Impossible to progressively enhance.
It does loads of magic such as dependency injection that is completely unnecessary. Computer programs are for explaining what is happening to the next person and Angular makes this more difficult.
There are other JS frameworks that do none of this and solve your problems, I say use them instead.
Data binding in particular had a really difficult to use syntax for things like looping. Loads of different attributes that you have look up every time you need to do something in a template.
Generally people have no idea what the in built components are doing of why the infinite scrolling can't work well on mobile.
It's really hard to use a JS debugger when I've tried with Angular - maybe you have had more success than me on this?
You admit Angular comes with some problems out of the box, how are new dev teams meant to know this automatically?
This doesn't make sense to me. AngularJS is the underlying code that is happening. Angular is a framework, not a language, it's just JavaScript and really quite simple once you wrap your head around the role of the injector, services, and service providers (whereas everyone seems to pick up modules, controllers and directives pretty quickly).
As we all know, abstractions leak, please avoid and use one of the many other libraries that people can actually follow through and debug without having to learn Angular first.
Abstractions leak, please avoid and use one of the many other libraries that also make heavy use of abstractions, the foundation of modern computing.
In the early days of Django the had a magic removal branch for this very reason; people who do not understand the framework will find it harder to learn.
This is just my experience. Maybe you are some sort of Angular savant who agrees with all the assumptions the authors made.
Haha, and your abstractions comment deliberately misses the point; of course we abstract things but you should use the simplest possible abstraction for the job. Angular is not that.
"lots of magic" and "difficult to trace compared to x" sound a lot more reasonable than claiming AngularJS is an abomination, so I'll address those claims instead. Yes, magic in programming can be dangerous, but keep in mind that magic always has a logical explanation. In this case, the magic turns out to be a pretty mundane case of metaprogramming. If a developer can't understand how it might be possible to pattern match parameter identifiers in JavaScript, then I think AngularJS DI is the least of their worries.
Maybe you are some sort of Angular savant who agrees with all the assumptions the authors made.
I'll take that as a compliment, but I'm just a regular guy who has pushed a few medium sized angular apps into production. I definitely don't agree with all of angular's design decisions, but I don't have to agree with everything to use the framework effectively.
In the early days of Django the had a magic removal branch for this very reason; people who do not understand the framework will find it harder to learn.
Contrast that anecdote with Rails, a framework that has very successfully embraced the power of magic from the start to create what is arguably the most influential web framework of the last decade. It's an opinionated approach, but not inherently wrong.
of course we abstract things but you should use the simplest possible abstraction for the job. Angular is not that.
The simplest solution isn't always the best solution, if that were the case we wouldn't be discussing client side scripting in any capacity.
Edit: I am not really into Angular so I was surprised to see such reflection on functions. Good to know there are a lot of solutions to that already.
http://docs.angularjs.org/tutorial/step_05 (see A Note on Minification)
myModule.controller('FooCtrl', function ($rootScope) {});
into the minification-safe: myModule.controller('FooCtrl', ['$rootScope', function ($rootScope) {}]);Consider the following syntax:
// values to inject
var registry = {
scope: ...
http: ...
}
inject http, scope {
http.get('/search', {query: scope.query}).then(render);
}
all of these can be done today with sweet.js[1] — see the example http://bit.ly/1bjdxWJ[1]: http://sweetjs.org