Development Central

Bill Sorensen's software development blog

AngularJS - is it worth it?

Over the last year and a half, I've been doing a lot of work on a single-page app (SPA) built with AngularJS (a.k.a. Angular 1). I didn't write the initial version, but I was part of a team developing new features for it.

Caveat: I don't consider myself an AngularJS expert.

That said, I plan to steer clear of AngularJS on my next project.

Why not AngularJS?

1. Testing.

There aren't a lot of options for doing end-to-end testing with AngularJS. The official recommendation is Protractor, which is built on Selenium. Protractor has its own learning curve, particularly in the way it hides asynchronous calls with "magic." The documentation recommends against using it with PhantomJS, so it's relatively slow.

We disabled all of our Protractor tests. Several team members (I was one) spent multiple days over the course of weeks trying to get the tests to run reliably on our build server. We gave up in frustration.

We switched to unit tests (Jasmine + Karma). These work reliably with PhantomJS. They aren't easy to write, though. Every component requires a different type of test. Want to test a controller? Look up how to do it. A directive? That's different. A filter? Different still. A service? Different. A directive with a template? Different - install and configure a library to handle the template cache.

http://angulartestingquickstart.com/ is helpful for untangling the complexity.

Recently Nightwatch.js appeared on the scene; I have heard it is possible to test AngularJS with this powerful tool.

2. Framework.

AngularJS is a framework, not a library. It attempts to provide solutions to nearly every aspect of building a web application. One cost of this approach is complexity. Tutorials may give developers the feeling that AngularJS is simple; it's not. Even after using it daily for months, I learned important facts on a daily basis.

Some aspects are needlessly complex. For example, nearly every time I use ngOptions I have to look up the syntax. One would think that creating a drop-down list from an array of objects and binding it to an identifier would be a common use case.

I distrust frameworks in general; they tend to be less flexible than building an application using focused libraries. If a God Class violates the Single Responsibility Principle, doesn't a framework suffer from the same issues?

3. Documentation.

I referenced the AngularJS documentation frequently during development. Then I'd go out on Stack Overflow or Google and try to find a clear explanation. The official documentation appears to have been written as a technical reference, and I personally find it difficult to follow.

4. Fragile.

I lost count of the number of times I forgot that myName becomes my-name in AngularJS. Except with filters.

Moving markup that worked perfectly into a template on a directive caused display issues that we never did resolve.

There are a number of other "gotchas" in the AngularJS world. Mistakes (such as typos) tend to fail silently. Part of this is the nature of JavaScript and dynamic languages in general, but it doesn't make things any less painful.

Here's one that took some time to track down: Angular $http calling success on 404

5. Short-lived.

Angular 2 is here. Much of what I've learned with Angular 1 will be obsolete eventually. How much study time do I want to spend on this? How long will it be around?

If you want to use AngularJS...

Follow the AngularJS style guide by John Papa. The guide is endorsed by the Angular team. If we had started with this, development would have been much less painful.

Avoid $rootScope whenever possible. Think of it like using global variables. Leverage services instead.

Use UI-Router. Don't even start with the AngularJS router. It will paint you into a corner of workarounds and hacks. This article opened my eyes.

What should I use instead?

I don't know. I like React's philosophy, but I'm still a beginner with that. I haven't tried other SPA frameworks (including Angular 2). Consider if you really need a SPA; would ASP.NET MVC plus a bit of Knockout do the job?

Whatever you choose, look for simplicity, testability, and clear documentation. Don't be sucked in by "look how fast you can build a to-do list!" samples.

Knockout.js learning tips

It's been awhile. I'm doing web development at my new job, so this and future posts may focus on that.

I used Knockout.js recently, and I'll share a few tips that I learned the hard way.

1. Watch the parentheses.

Remember that ko.observable objects are functions. If you're binding to the property and nothing else, you can omit the parentheses. If an expression is involved, you'll generally need them. If in doubt, include them.

The easiest way to avoid the need for parentheses is to put as much logic as possible in the view model. This minimizes expressions in the markup.

2. Be careful mixing server-side and client-side code.

This was on an ASP.NET MVC site, and we had both view engine markup and Knockout bindings originally. This proved difficult to reason about. While it's definitely possible, remember that Knockout is only going to see the page once it's rendered client-side.

3. Avoid comment (containerless) Knockout bindings.

I found that these did not seem to play well with templates, and they may not work with IE8.

4. Don't mix if bindings with other bindings.

I combined an if and a text binding in the same element. This resulted in an error of "You cannot use these bindings together on the same element." One solution is to use another span or div.

5. Don't mix if bindings with CSS classes or other markup.

In general, use if bindings with a div that has no classes, etc. The issue is when the binding is falsy, the element will still render - it'll just be empty. Styles can cause undesired visual artifacts.

6. Be cautious if mixing jQuery and Knockout.

We were using jQuery to wire up form submission to a class that was in a Knockout foreach. It wasn't working. The fix was to switch to a Knockout submit binding. Knockout can work fine with jQuery in most cases, though.

7. Remember what binding context you're in.

Especially with foreach, it's easy to forget and bind to the current item when you meant to bind to $parent or $root. The Knockoutjs context debugger (search the Chrome store) can help.

8. Don't try to do progressive enhancement.

If the client doesn't have JavaScript enabled, skip the whole Knockout section. See http://stackoverflow.com/questions/8961073/progressive-enhancement-with-knockoutjs.

9. Encode where appropriate.

It appears that attr bindings don't encode anything (although the text binding does). This is particularly relevant when binding to the href attribute of an anchor.

10. Use foreach on the parent element.

The documentation is clear on this, but it's easy to misread. The result of binding to a child is typically missing closing tags.

I like Knockout, and the learning curve isn't very steep. Keep it simple and it seems to work well.