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:
- assignments (
=
,+=
,-=
, ...)new
- chaining expressions with
;
or,
- increment and decrement operators (
++
and--
)
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!