Blogs

AngularJS: The Bad and the Better

Overview

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.

My previous experience had me working with plain Javascript, jQuery, and (mostly) Sencha and Sencha Touch. I initially thought that AngularJs was Just Another Javascript Framework, but I've since decided that I heartily enjoy it, and would recommend it in many common web development scenarios, such as writing better forms and interactive interfaces.

AngularJS forces you to write declarative HTML and testable Javascript. No, stop; re-read that sentence, because it didn't sink in yet. You've probably been writing templates in languages that allowed you to program in them: you know, languages that allow you re-define variables, have complex if-elseif-elseif-elseif statements, and define functions. Well, with AngularJS all that shit is gone. THANKS GOOGLE! In AngularJS you can't fix data in the fly on your templates, unless it is being formatted by a filter. You get data with your services or factories, and you set up template usable functions in your controllers. Your controllers depend on your services and factories. Your tests verify that your controllers can't magically assume shit, like that you have a window object, because you're supposed to be using the injectable $window instead, which is actually testable.

Good stuff.

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.

Examples

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 $scope, $window, and $location.

If you happen to accidentally scroll past the article and see the comments, you'll see the community at its worst. Developers, frustrated with their lack of understanding, take it all out on the AngularJS team. If you go into the Javascript chat room on Stack Overflow, you'll see endless requests for help from AngularJS experts, with nary an answer in sight. The same thing is seen on twitter. I don't know where the AngularJS experts are, but I haven't found them yet.

Everything, and I mean everything, in AngularJs needs more examples.

Dog at a keyboard with tongue hanging out
Me, on a bad day, with AngularJS

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.

Bad: Dumping More Javascript into Your Project Haphazardly

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.

I already have a big website with lots of code, and a fair amount of Javascript. So it would seem to make sense to add my Javascript to the existing pile. It does not; don't fall for this trap. You want to make use of unit testing and dependency injection and third party libraries, and, well...a lot of other crap that Angular provides that your old code won't know what to do with.

Better: Ng-Boilerplate

AngularJS Best Practices: I've Been Doing It Wrong! is a good series on how to get started with a more complex layout. You are writing restful APIs for your Javascript code, right? So you don't really need to house your Javascript with the rest of your source hidden away in a scripts folder. It can be located wherever you want in your source tree, and then get built by Grunt (linting: good! minimizing: good!), copied to wherever you need it to be. Then the unit tests can run automatically with the Karma test runner in the PhantomJS headless webkit browser. Get familiar with Grunt, and you'll be forced to write better Javascript.

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?

Grunt is a task runner that runs javascript on NodeJS. NPM is a package manager for NodeJS. You can use it to get plugins for Grunt, like one that compiles your less or sass into css, or that fingerprints your files, or lints your javascript.

Side Note: What is Bower?

Many projects these days depend on other Javascript projects. Anyone who has recently tried to migrate to a new version of jQuery will know the pain of trying to track down all the various libraries and their versions. This is the problem that Bower, written by Twitter, solves. It provides a location in a source repository and a version spec file (.bower, now) that tells your project what version you currently have and how to mechanically discover whether there are upgrades to your Javascript library and how to get them. Bower runs on NodeJS.

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!

Wrapup

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!

Syndicate content