Pitfalls in Angular2+

July 2017
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);
}

@Input set myVar

It is quite handy, that you can have side effects on an @Input variable. The docs is calling this concept "Intercept input property changes with a setter". However, this comes with a huge problem. If you're e.g. trying to fetch some data through http.get when a new value is passed in, this causes an infinite loop.


I'll update this article constantly as I run into more pitfalls during development, so stay posted by subscribing!
Dear Devs: You can help Ukraine🇺🇦. I opted for (a) this message and (b) a geo targeted message to Russians coming to this page. If you have a blog, you could do something similar, or you can link to a donations page. If you don't have one, you could think about launching a page with uncensored news and spread it on Russian forums or even Google Review. Or hack some russian servers. Get creative. #StandWithUkraine 🇺🇦
Dear russians🇷🇺. I am a peace loving person from Switzerland🇨🇭. It is without a doubt in my mind, that your president, Vladimir Putin, has started a war that causes death and suffering for Ukrainians🇺🇦 and Russians🇷🇺. Your media is full of lies. Lies about the casualties, about the intentions, about the "Nazi Regime" in Ukraine. Please help to mobilize your people against your leader. I know it's dangerous for you, but it must be done!