Angular Directive Grammar & Microsyntax: Demystifying the Hidden Parts
Khang Tran β‘οΈ
Posted on November 3, 2023
Introduction
<div *ngFor="let leaf of ['π', 'ππ', 'πππ']; let i = index;">
<p>{{i + 1}}. {{leaf}}</p>
</div>
1. π
2. ππ
3. πππ
If you've worked with Angular, I'm sure you've come across this *ngFor
syntax at least once: let leaf of ['π', 'ππ', 'πππ']; let i = index;"
. However, have you ever wondered?
- Is there any alternative
*ngFor
syntax? - Where does the
index
variable come from? - How does the
leaf
variable iterate over and reference items of['π', 'ππ', 'πππ']
? - Why is it
let ... of ...
? Can we change it tolet ... something ...
?
What do you think about trying the following syntaxes with
*ngFor
?
1. let leaf of ['π±', 'π', 'πΏ']; let i = index;
2. let leaf; let i = index; of ['π±', 'π', 'πΏ'];
3. let i = index; let leaf; of ['π±', 'π', 'πΏ'];
4. let i = index let leaf; of: ['π±', 'π', 'πΏ'];
5. let i = index; of ['π±', 'π', 'πΏ']; let leaf;
6. let i = index; of: ['π±', 'π', 'πΏ'] let leaf;
7. 'Magic πͺ'; let i = index; of: ['π±', 'π', 'πΏ']; let leaf;
8. let leaf = $implicit of ['π±', 'π', 'πΏ']; let i = index;
9. let leaf = $implicit; let i = index; of ['π±', 'π', 'πΏ'];
10. let leaf = $implicit, let i = index, of ['π±', 'π', 'πΏ'];
You can quickly find the answer via this StackBlitz link.
Surprisingly, these peculiar syntaxes
compile without any errors. π€
How does this happen? What are the rules behind these syntax variations?
Demystifying *ngFor syntax
At first glance, you might be under the impression that you're required to use a semicolon
to delimit the calls and adhere to a certain order
, or that there might be more rules
you don't yet understand about how to use the syntax. But that's not the case - the syntax is actually quite flexible more than that.
Angular's microsyntax has 4 building blocks, that when combined in a particular way, make up the entire microsyntax API. These building blocks are:
- Expressions
- The
as
keyword - Keyed expressions
-
let
bindings
1. Expressions
Anything that, when referenced, returns a value.
- Raw value
<p *hello="'π Hey there'"></p>
- Calling a function
<p *hello="greeting()"></p>
- Referenced a variable
<p *ngIf="shouldDisplay"></p>
- Operator (
9 * 9
)
2. The
as
keyword
The rules behind the as
keyword as an alternative to let
. The most commonly used is combining between *ngIf
and AsyncPipe
.
<div *ngIf="(myFutureGirl$ | async) as myFutureGirl">
{{myFutureGirl}}
<div>
Oops, 404 π€¦ββοΈ
3.
keyExp
- Key Expressions
A key expression is simply an expression that you're able to bind to an input on a structural directive.
For example, the *ngFor
directive includes an ngForOf
input as follows:
/**
* The value of the iterable expression, which can be used as a
* [template input variable](guide/structural-directives#shorthand).
*/
@Input()
set ngForOf(ngForOf: U&NgIterable<T>|undefined|null) {
this._ngForOf = ngForOf;
this._ngForOfDirty = true;
}
In this case, the key expression is the of
keyword. This directly answers the question, Why is it let ... of ...
?
4.
let
bindings
The let
binding is used for reference the Template Context
. Let's take a look at our example:
let leaf of ['π±', 'π', 'πΏ']; let i = index;
There are two types of references
made using let
bindings:
- Implicit
In this case, the assigned expression on the right side is omitted, and it is equivalent to the following:
let leaf = $implicit
Angular automatically assigns the reference with the $implicit
key in the context.
- Explicit
let i = index
When we use let i = index
, it is clear that the variable i
references the index
key in the context.
Combining Things Together
- It starts with the * reserved token. It is an Angular special syntax companion with the Angular Structural Directive.
- Then, you have to declare the
selector
value of the directive itself. - Finally, you can bind to the
selector
as with any other input using the=
token.
The contents of the input itself are where the microsyntax goes.
1. First Item
Either an expression
or a let
binding.
If an expression
is passed, the value of the expression will be passed to the same input name as the selector
itself.
Let's take a look at *ngIf
implementation:
@Directive({
selector: '[ngIf]',
standalone: true,
})
export class NgIf<T = unknown> {
/**
* The Boolean expression to evaluate as the condition for showing a template.
*/
@Input()
set ngIf(condition: T) {
this._context.$implicit = this._context.ngIf = condition;
this._updateView();
}
}
*ngIf
includes an ngIf
input that is the same as the [ngIf]
selector itself.
If a let
binding is the first item, it will work exactly as it's explained in the previous section.
<!-- β
These ARE valid for the first item -->
<p *ngIf="'Expression'"></p>
<p *ngFor="let i = index; ..."></p>
<!-- π But these are NOT valid for the first item -->
<p *ngFor="of: ['π±', 'π', 'πΏ']; ..."></p>
<p *ngFor="index as i"></p>
2. Second Item and Beyond
After the first item, youβre able to pass in a let binding, an as binding, or a key expression.
Let's revisit these *ngFor
syntaxes at the very beginning of this post:
1. let leaf of ['π±', 'π', 'πΏ']; let i = index;
2. let leaf; let i = index; of ['π±', 'π', 'πΏ'];
3. let i = index; let leaf; of ['π±', 'π', 'πΏ'];
4. let i = index let leaf; of: ['π±', 'π', 'πΏ'];
5. let i = index; of ['π±', 'π', 'πΏ']; let leaf;
6. let i = index; of: ['π±', 'π', 'πΏ'] let leaf;
7. 'Magic πͺ'; let i = index; of: ['π±', 'π', 'πΏ']; let leaf;
8. let leaf = $implicit of ['π±', 'π', 'πΏ']; let i = index;
9. let leaf = $implicit; let i = index; of ['π±', 'π', 'πΏ'];
10. let leaf = $implicit, let i = index, of ['π±', 'π', 'πΏ'];
In fact, these *ngFor
syntaxes end up with the same result:
<ng-template ngFor let-leaf [ngForOf]="['π±', 'π', 'πΏ']" let-i="index">
...
</ng-template>
Optional Separators
One side note, maybe you've already figured out from these *ngFor
syntaxes above.
Just as the :
is optional in the a key expression
, all separators in the micro syntax are optional.
π Hey there
If you want to gain more insights into how Angular Structural Directives internals, you can reference my previous blog post Demystifying the Angular Structural Directives in a nutshell
Thank you for making it to the end
Thank you for choosing to read this blog post among the tens of thousands of great blog posts out there. And I will be really happy if you find something interesting from my blog post.
Reference
Posted on November 3, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.