"This has structure. I'm hearing structure."

- Contact (1997)

One of the crazier-looking things about Angular is the structural directives. Namely: *ngIf,*ngFor and *ngSwitch. As you might have noticed, the leading asterisk is the calling card of these directives. Why the asterisk, you ask? Well, you can read the official angular explanations of you're really curious; but I suggest you just roll with it for now.

Don't let the looks deceive you, though. Once you get the hang of it, this is some severely cool stuff.


*ngIf:

Like I said before, Angular has brilliant little shortcuts for frequently encountered use cases. Once of the most common one is hiding/showing an element, or a component, at specific times. *ngIf lets you do that, all you need is a boolean specifying your condition. *ngIf will show your stuff when the boolean is true and hide it when it's false. The syntax is pretty similar to data binding.

That means, you could either bind the directive to the keywords true or false to make it permanent, or you could bind it to a variable or a statement that evaluates to a boolean value, and control visibility status accordingly.

For example, in our kick-ass.componennt.html let's show an <p> element when the button is clicked. Enough with the annoying alerts!

<p *ngIf="false" [style.color]="isReady ? 'navy' : 'red'">
    {{getProfile()}}
</p>
<button [class.afterFirstHit]="!isReady" (click)="gotHit()"> Hit Me </button>
<p *ngIf="showLine"> You hit like a vegetarian. </p>
<br>
<br>
<button *ngIf="false" [disabled]="isReady" (click)="gotHitAgain()"> Hit Me Again </button>

Note that I've bound the first <p> and the second button to the value false so they won't be visible.

The interesting stuff is happening here:

<p *ngIf="showLine">...

Again, the stuff inside the quotes is evaluated and applied. We'll need that variable in our .ts file:

    ...
    showLine: boolean = false;

    ...

    gotHit() {
        // alert('You hit like a vegetarian');
        this.isReady = false;
        this.showLine = !this.showLine;
    }

Just for kicks, we're toggling our boolean every time we click the button. So it should show on the first click, hide on the second, and so on. Try it.


*ngIf vs [hidden]

A key point to be noted here is that the hide/show stuff *ngIf does can also be accomplished via the CSS display property or the HTML hidden attribute. In effect, the following three lines are equivalent:

<p [style.display]="showMe ? 'block' : 'none'">
<p [hidden]="!showMe">
<p *ngIf="showMe">

However, there are subtle differences I'd like to point out:

Syntax differences:

Obviously, *ngIf looks the cleanest. But apart from that, the thing to remember is that the *ngIf shows the element when the boolean is true; while the [hidden] hides it when the boolean is true. Be sure to account for that if you ever find yourself switching between the two. (style.display doesn't need any changing as it plays by the ternary operator rules.)

Semantic differences:

This is a crucial distinction: unlike the HTML and CSS solutions, *ngIf doesn't simply hide the element. It removes it from the DOM entirely.

The advantage of doing that is elements that are meant to be hidden don't take up resources. Otherwise, all the data and event bindings of the hidden components would be intact and take a toll on performance. and That's why many, many bloggers would frown upon using [hidden] and command that you use *ngIf instead.

But it's not that simple. The trade-off here is that *ngIf triggers creation and destruction of your component, which can be expensive in itself. More expensive than the savings in resources, possibly.

What I'm trying to say is that there's no one right answer. [hidden] should be preferred in some scenarios; *ngIf in others. Use your best judgement.

Tip: *ngIf is not always the right answer, even though it should be preferred in most simple cases.

Of course, in my experience, there are cases where the component you're trying to hide contains something your visible components absolutely need to refer to. In that case, removing it from the DOM using *ngIf simply isn't an option. You have to use [hidden].


*ngFor

I've saved the best for the last. If the name didn't give it away, *ngFor lets you run a loop, that creates components. This is powerful stuff.

With great power, however, comes complicated syntax. Let's take a look.

First, let's define an array called hitList in our .ts file. We need something to loop over.

    ...
    hitList: string[] = ['Joffrey', 'Cersei', 'Ilyn Payne', 'The Mountain', 'The Hound']
    ...

Now, in our .html template, we can have:

<p *ngFor="let person of hitList" > {{person}} </p>

Observe the stuff inside the quotes here. This is a different beast than the simple variables/methods we've encountered so far. Angular dubs this the microsyntax.

Basically, *ngFor is looping over hitList. person is a temporary variable created and accessible to the template inside the loop. It holds the value of the current item in the iteration -- in our case -- the current string in the array.

Now, we're interpolating that variable later inside the tags. In effect, the loop will run 5 times and create 5 <p>elements. Each element will have the value of the string we configured.

Bear in mind, though, you have to define *ngFor on the component you want created; not on it's parent. Say you wanted checkboxes next to each item in the list. The code would look like:

<h1>Hit List</h1>

<div *ngFor="let person of hitList">
    <input type="checkbox"> {{person}}
</div>

Now, the person variable is still accessible inside the template. But now, it's the <div> element that's being repeatedly created. Along with it's children, of course. So it's not 5 checkboxes in one <div>. It's 5 <div>s with their own checkboxes.

If you want to keep track of the index you're at while iterating, there's a provision for that too. You can assign a variable to the index property like this:

<div *ngFor="let person of hitList; let idx=index">
    <input type="checkbox"> {{idx + 1}}. {{person}}
</div>

Note that (a) the idx has to be interpolated separately and (b) the indexing starts with 0.

Before sharing a screenshot, let me change the background-color of our component real quick so it stands out.

Done.

Your page should now look like this:

There are several more options like index to play with. You can explore them here.

Again, this is just a small demo. The real power of *ngFor comes into play when you're creating multiple custom components. You can pass any iterable data to your component and create children by looping over it. We'll revisit this when we see component interaction in action.


Here's a plunker with a live demo of what we've done in this section.

results matching ""

    No results matching ""