AngularJS: The Bad and the Better
A few months ago, I started an exciting new job working with lots of awesome technologies. Some of the more excellent parts of the stack I get to play with are Go and AngularJS. I don't feel I know enough Go to say hardly anything intelligent yet other than that I think it is a very interesting and useful language. I've used AngularJS significantly more though, and so I'd like to share a few of the things I had to learn the hard way.
AngularJS is deceptively simple on the surface when you try it out in the tutorial. Once you start understanding all the things it does for you though, it will blow your brain to try and grok how it all works, and that is when you can start building truly complex things with AngularJS. This post isn't really about convincing you to use AngularJS though. I feel like the tutorial does a pretty good job of that. This post is about how to use AngularJS in a production environment where you already have lots of non-Angular code.
Bad: AngularJS Documentation
While AngularJS has a significant amount of documentation, the quality is spotty at best. I understand that they recently made their documentations available in a repository on github that allows pull requests, in attempt to make use of community knowledge. I found the tutorial on the home page to be useful for getting started, but only occasionally am able to make good use of the Developer Guide or the API Reference.
Dear Google: with documentation, unlike code, platforms, or language features, less is not more. More is more. Dependency Injection in Angular is a wonderful, glorious thing, but the article on it has far too few examples. I need to know how to write my own dependencies for Angular and get them injected, not just how to use the dependencies they've prepared like
Everything, and I mean everything, in AngularJs needs more examples.
Better: AngularJS by Brad Green & Shyam Seshadri
AngularJS is an excellent book that will really help you grok how AngularJS works. I recommend that you start with the tutorial on the Angular home page, and then do a quick read-through of this book. Don't bother yet trying to understand all the things they are trying to tell you. Then try and write some small applications. When you do, you'll find that you have lots of questions. This is when a second read-through of this book is handy.
Update: I generally hate video tutorials, but these very short tutorials created by John Lindquist at egghead.io are great. In addition, egghead.io has transcripts of each tutorial if you just can't stand the video or have no headphones at work. Find what you are looking for, and watch them. It will be well-worth the time.
The AngularJS book won't really be enough, but it will be a good starter.
AngularJS begs to be done right. It wants you to componentize things, make directives, unit test, end to end test, manage dependencies with dependency injection, and the whole farm. So if you try to just shove it into your project without a plan, you'll quickly start finding yourself disgusted with how hard it is to manage the structure that AngularJS wants you to use.
Ng-Boilerplate isn't the only name in the game for project layouts. There's also Yeoman, which sounds interesting, but I haven't tried it personally. I don't know how well it would work to mix layouts, and Yeoman seems like it more intended as a scaffold than as a general project ordering.
Side Note: What is NPM?
Side Note: What is Bower?
Side Note: What is PhantomJS?
You want to test things in a real browser each time you make changes to your file. You don't want to see that brower pop-up, because your test results are all programmatically determined. This is what PhantomJS gives you. It does other stuff too, like taking screenshots or monitoring page loading.
Bad: Loading Your Initial Page View With AJAX Calls
Here's a common scenario with AngularJS: the user visits your page, and is greeted with a weird layout, that eventually resolves itself to what you intended. AngularJS has to do a large number of passes on your scopes to ensure that your models have "settled". So your visitor is confused, because the HTML seems to have rendered, wrongly, but then gets the AJAX data, re-runs your models, and everything updates to look fine.
Better: Ask Your Services To Talk To $window
You're already talking to your server, and know what you should get, because you already had the user interaction (the page visit). What I do is I get my initial load data server side, and then pass into Angular by attaching it to the window object. Then in Angular, I have a service whose job it is to fetch that information from the window and provide it to my app in a dependency injectable way. Because
$window is injectable, I can then test by attaching whatever input data I want.
Bad: Using a Module for a Directive All By Itself in Non-Angular Code
If you're like me, you've written a few directives that do badass things all by themselves that you'd like to integrate with your regular non-Angular views. So you drop them into the view before the first of any of your Angular directives is supposed to be loaded, and by golly, that directive renders like magic from the gods. You feel stellar - until you decide you want to add a second directive. You look up ng-app within ng-app and get confused. What now?
Better: Make a Generic Module for use with Regular Code And Depend on Modules with Directives
This one is pretty simple: just make a parent module that you are going to use for all your non-AngularJS views. Then make that module depend on any modules that provide directives that you want, and set that module as an ng-app at the top of your page. Done!
Bad: Passing Around Too Much Data With Your $routeProvider
$routeProvider is a pretty useful construct for switching what controller you are in simply and easily. It also allows you to pass in strings as a parameter to the controller in the definition for each route. Eventually you'll start trying to pass all sorts of data to your controllers, because this is what you do in a regular server side app with user state or user supplied information. This is wrong, obviously, because AngularJS isn't actually switching pages, and you really don't need to pass that data to your controller.
Better: Use Your Services To Get Your Data
Because the page hasn't actually changed, you still have access in your new controller to the same $rootScope. Make sure that both controllers depend on the same data service, and then in the first controller, before switching, ask the service to store whatever you need. It can then deal with how that information is persisted, whether in $rootScope, passed to the server, set in a cookie, local storage, stored in a variable, or whatever. The second controller will then ask the service for the data it needs, and regardless of how complex it is, you have your data back.
There's actually an awesome tutorial on egghead.io about this that I just found. Check it out!
AngularJS is awesome, and you should probably use it. AngularJS is hard to really get to know because it is a complex piece of software, and I don't know how to fix that. I hear there's an AngularJS conference coming up. Wow!