Why Angular 2 Is So Awesome

Jacob Gardner
6 min readMay 10, 2017

--

Did you want to know what’s so great about Angular 2? Man, that sure was rhetorical. I’m definitely going to tell you regardless.

Router Time

Let’s talk routing; every app worth its salt needs routing. First you start with a simple route that looks something like this:

{
component: SweetComponent,
path: 'fabulousness/:someId'
}

Pretty straightforward, yeah? You’ve got a route, it takes an id, renders that component and the component gets to do something with the id if it wants. There are a few ways that you can get to it. One would be to render a link pointing to it:

<a [routerLink]="['/fabulousness', thingy.id]">Click me, sucka</a>

This will happily append whatever that thing’s id is to “/fabulousness/” and work just as you’d expect. “But what if you want to add query parameters?” you ask me with an exasperated look on your face. Well don’t you worry your pretty little head; that’s only slightly more complicated:

<a [routerLink]="['/fabulousness', thingy.id, { otherStuff: '123' }]">Click me, sucka</a>

Alright, that’s pretty swank. How about we look at making that same navigation from in a component?

router.navigate(['fabulousness', thingy.id], { queryParams: { otherStuff: '123' } })

Gee, I sure do love it when my APIs are arbitrarily inconsistent. Why include the params in an array like the template syntax when you could pass them as a named key of a second argument? Okay, time to dig down into that SweetComponent from earlier and see how it accesses those fields.

Your route is polite enough to provide Observables so that the component can make any necessary updates when the route changes, like this:

route.params.subscribe(({ someId }) => ...route.queryParams.subscribe(({ otherStuff }) => ...

Convenient, huh? Well, how about if you don’t need to worry about changes, and instead just want to grab one of those values synchronously right now? You can do that too, with route.snapshot.params and route.snapshot.queryParams. Oh, but both someId and otherStuff will be properties of params, while queryParams will be an empty object. What, did you want your framework to be intuitive and consistent? This is the future!

Detect Them Changes

How about when you want to be notified when the input properties of one of your components change? Well then you’d just have it implement the OnChanges interface, and add a method that looks something like this:

ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
...
}

Damn, that’s a mildly gnarly method signature. Simply put, it’s a hash of the names for component properties that have changed to corresponding SimpleChange objects. A SimpleChange instance has two properties on it: previousValue and currentValue. “But Jacob,” you ask, frantically, “Why include both values when one could simply be accessed with this.someProp like you usually do? Couldn’t one value just be the corresponding value of the hash, and aSimpleChange instance could be left out entirely?” Why yes, silly goose, that would work just fine. But over-engineering things is how we prove how smart and educated we are. Why pass around simple values when you could encapsulate them in completely redundant classes? Perhaps this is to make things more TypeScript-y. On that note, I’ll just defer to a passage from my entirely hypothetical and nonexistent previous article, Why TypeScript Is So Awesome:

Unfortunately, TypeScript’s philosophy tends to encourage a developer to encapsulate values in ES6 classes, much as one would use structs in other languages. Doing so, however, is disingenuous to the point of being hazardous for new JavaScript developers. It creates the illusion that they are creating an actual type, when this is of course impossible in JavaScript. A class, regardless of whether it is simply a bundle of values or full to the brim with methods and interfaces, is still just a function. An instance of a class is just an object that inherits from that function’s prototype. When we obfuscate the true nature of JavaScript’s dynamic typing and prototypal inheritance behind a facade of static types and classes, we directly harm inexperienced developers by giving them a false impression of how JavaScript actually works. This is clearly damaging to the JavaScript community and downright dangerous for employers.

Where Are We Going with This?

Angular’s approach to front-end web development is, to quote Dr. René Belloq, to “use a bulldozer to find a china cup.” Speaking of the aforementioned change detection, let’s dive into how that works for a bit. All you need to do is update a property on an Angular component, like so:

this.myState = '...'

and the changes will be reflected in that component’s template. I have to be honest, the first time I encountered this type of syntax in Angular 1, I was blown away. “All I need to do is update this property, and it’ll be automatically updated in the DOM?! It’s like magic!” Which leads me to this relevant point:

“Magic” in software is bad. “Magic” implies a fundamental lack of understanding for how something works.

When something appears to be magic, it is typically because the interface it presents to a developer is so alien compared to what they’re used to that they can only conclude that its implementation is beyond their understanding or comprehension. This is harmful to any developer’s learning experience.

Let’s demystify this a bit. Angular 2 utilizes zone.js in order to encapsulate asynchronous functions in their own execution contexts, which allows it to perform change detection whenever an asynchronous event occurs. For example, when a click event triggers a callback within a component, that callback will run within a Zone, which can subsequently notify Angular when the click callback has completed. Zone.js achieves this by wrapping asynchronous operations like user interface events, timers (e.g. setTimeout), and XMLHttpRequests. Now, glossing over the fact that monkey-patching Web APIs gives me a sensation similar to what I imagine removing bubonic plague sores with a metal file must feel like, there are some other things that I think must be considered here.

First of all, the method itself is not foolproof. Developers just getting into Angular 2 could very often find themselves using ref.detectChanges() in cases where the change cycle doesn’t seem to be occurring on its own. This can be especially prevalent while using certain third party libraries that could potentially run code outside of the Angular Zone. By introducing an unnecessarily complex layer of abstraction around change detection, Angular creates the possibility for numerous frustrating bugs, and forces the subsequently frustrated developers to fall back on manually forcing a process that should be automatic.

Angular’s method is to allow you to merely update some property on your component, and then by a complicated system of branching execution contexts, determine when that component should be checked for changes, subsequently detect that you updated a property, and reconcile that change with the DOM. Now, I’m sure if I presented the challenge of coming up with a simpler method of notifying the component when to perform a change detection cycle, it wouldn’t take much time for you to give me an answer. How about we just call a function when we want to update something in the component’s state? Something like, I don’t know, this?

this.setState({ myState: '...' })

I’ve been trying up till now to ignore the elephant in the room, and in case you were wondering, that elephant is wearing aviators, can play the guitar part of “Freebird” start to finish without making a mistake, and is named React. React has no need for monkey-patching or execution contexts because all that needs to be done to trigger an update cycle is to call the setState method. Aside from being much simpler in implementation, there’s a key difference that makes this method more intuitive. When looking at a simple property assignment like this.val = ..., the average JavaScript developer wouldn’t expect anything else to be occurring as a result of that statement, until its value is read somewhere else later. This is why Angular’s change detection cycle seems at first glance to be magical. On the other hand, this.setState(...) is a function call whose return value is not used, implying that this expression will have side effects. There is no mysterious behind-the-scenes detection going on here; it is quite clear that such a function will produce side effects in the form of template reconciliation. React eschews magic for simplicity and clarity.

Just for the sake of another comparison, let’s reexamine that previous example of updating a component whenever its inputs are changed. In React, a component need only implement this method:

componentWillUpdate(nextProps) {
...
}

To perform a comparison between changed values, check this.props.val versus nextProps.val. No need for excess abstraction. It’s minimalist and intuitive.

Angular 2 embraces the philosophy of over-engineering to its fullest degree. It presents a truly massive and complex API coupled with its own template DSL which is mutually exclusive to any other framework. React embraces idiomatic JavaScript and the philosophy of simple, composable tools. To paraphrase probably a lot of people though I can’t find any individual source at the moment:

Learning Angular will make you a good Angular developer. Learning React will make you a good JavaScript developer.

--

--