Pitfalls in Angular2+

Angular 1 has been complex, intertwined and error prone. "Is what I'm doing updating my data?", "Why doesn't this work?", "WTF is this?" or "GNARRRFL I wrote myObject instead of my-object again!!" might have been common things you have said developing an angular 1 app. So what about Angular 2? Is it all chocolate and honey bears from there on? The answer is no. There are still quite a few pitfalls you'll have to be aware of. Let me warn you of some here.

Template Syntax

Angular now allows less in the templates. What's most confusing is what may and may not go into bindings / expressions.
PITFALL 1: Javascript in templates is not really javascript!
We write template expressions in a language that looks like JavaScript. Many JavaScript expressions are legal template expressions, but not all.

This is done so you have to learn as little new as possible (since you already know JS syntax) while at the same time restricting the language to provide safety. At the same time the similarity to JS makes it less obvious what will work and what not.

JavaScript expressions that have or promote side effects are prohibited, including:
(and more) ProTip from Angular how to dodge problems with this:
A property name or method call should be the norm.

Okay, now you think you know it all about the js in the view? That's where you're wrong. In "Statements" (the stuff from the round brackets ()), there's yet another javascript like syntax that isn't really javascript!
PITFALL 2:

The template statement parser is different than the template expression parser

meaning

()="someExpr" and []="someExpr" have different allowed "someExpr"
An example:
[blub]="x = 3" // totally not okay
(blubbl)="x = 3" // totally ok
(blarb)="console.log('hello')" // totally not okay

I recommend to just stick with the above recommendation (only use function calls) and you should be pretty fine.


Anyways, let's move on.
PITFALL 3: Forgetting []
It's such a common pitfall because this wasn't required in Angular 1. Now it is. After working a while with Angular2+, this is still one of the pitfalls I tap into most frequently. As Angular states:
The HTML attribute and the DOM property are not the same thing, even when they have the same name.
This means <button [disabled]="isUnchanged">Save</button> will work, but <button disabled="isUnchanged">Save</button> won't work as expected. You can find more information here. Basically the quirky () [] syntax is justified in about 2000 words there.
And one more I recently stumbled upon: You can't have single { in your templates. For example, if you want to describe some code and write:
if else statements work like this:
if (condition) { ... } else { ... }
in your template, Angular won't like that. You can either escape each curly brace it in the most peculiar fashion:
"{{ '{' }}"
Fortunately the Angular error report is good on this one, so even when you walk into the pit, you'll get out fast.

CSS

I think it's awesome how we can write css / scss in Angular 2: directly in the components and it's not leaking around. The way it's implemented is that Angular adds name-spacing to emulate shadow-DOM-like behaviour. Yet, this introduces some problems when changing elements outside of Angular, e.g. with jQuery or something like d3js. For example, if I replace <div id="my-chart"></div> with d3 rendered html, the styles don't get applied. You now have two options. Option one: put the styles into the global styles.css file. Option two: prepend all styles with :host /deep/ .  See also this related stackoverflow answer. Of course that's a bit hacky, but that's how it is.

State Management and Data Binding

State management doesn't have a magical solution in Angular 2 and still requires serious consideration and planning when you write your app. It's a topic too big for this pitfalls-post, so here are some links to other posts concerning state management to get you started:

Observables

Take care when subscribing to observables that should encapsulate http requests. If you subscribe to them multiple times, the http request fires multiple times! Example:
createResource(newResource: Resource, resourceName: string): Observable<Resource> {
  const $data = this.http.post(this.resourcesUrl(resourceName), newResource)
    .map(resp => resp.json().data);
  return $data.catch(this.handleError);
}
and  somewhere else:
const heroObs = this.heroService.createHero(this.newHero);

heroObs.subscribe(newHero => { ... do thing 1
heroObs.subscribe(newHero => { ... do thing 2
now you create two new resources!

The solution to this is to to call .share() on the observable, so it would read

createResource(newResource: Resource, resourceName: string): Observable<Resource> {
  const $data = this.http.post(this.resourcesUrl(resourceName), newResource)
    .share()
    .map(resp => resp.json().data);
  return $data.catch(this.handleError);
}

I'll update this article constantly as I run into more pitfalls during development, so stay posted by subscribing!

Interested in full stack TypeScript?