Angular ngIf in Depth
Angular ngIf in Depth

In this post I want to show you what is ngIf inside Angular and how you can use it. And actually you might think now "but this is super basic stuff. I know how to use ngIf". But actually there are quite a lot of advanced features, that you also must know.

What is ngIf

So, the first question is what is ngIf? It is a possibility to write inside our Angular templates if conditions like inside JavaScipt.

<div *ngIf="true">Fooo</div>

So, we can wrote a div, and inside we have *ngIf attribute which will render this element if the condition is true. You should not forget to put * symbol before ngIf. Also it is important that it is written in camel case.

Inside we typically write some expression. For example here we can just write true. As you can see in browser we see our Fooo string.

<div *ngIf="false">Fooo</div>

If we write here false obviously we don't see this div at all. But now the question is what happens inside our elements.

Ng if in DOM

As you can see we have app-root and h1 tag inside. And actually as you can see our element was not rendered at all. You can see just the comment and this comment is for us as developers to know that here we have an if condition.

Which actually means it doesn't work like CSS. Our element is removed completely from DOM or added again in DOM, when ngIf is true.

Pass an expression

The next important point to remember, that we pass inside ngIf an expression. Which actually means that we pass inside a string and it is an expression which will be evaluated by Typescript. In our previous case false inside *ngIf is not a string it is an expression.

We can pack any valid Typescript code inside *ngIf and it will evaluate it

Which actually means if here we write 0 we won't show this div.

<div *ngIf="0">Fooo</div>

Because we are evaluating zero and as you can see here our element is not rendered.

<div *ngIf="0">Fooo</div>

But if we have here 1 and this is a number, then our element is rendered, because this value exists.

The same is with string.

<div *ngIf="'Some string'">Fooo</div>

If you want here to pass a string, you must write it like a string. Which actually means in double quotes we are writing single quotes and a string inside. In this case it will also be rendered.

The same goes for an empty string.

<div *ngIf="''">Fooo</div>

As you can see empty string does not render an element, because the empty string is false.

But you must remember just like in plain TypeScript if we write an empty object or empty array it will be true.

<div *ngIf="{}">Fooo</div>

Because here we just evaluate that object exists.

And obviously if you have an object with some value then this element also will be rendered.

<div *ngIf="{a: '1'}">Fooo</div>

And now you might ask me: "Ok we have so many possibilities, what is the best practice here?"

And actually the best practice is simply to use just a single boolean and nothing else.

<div *ngIf="isOpened">Fooo</div>

Which actually means if we want to write some condition, we need to create a boolean for this inside our component. Here we wrote isOpened and it means that we must define it is component. It is super easy to read, you don't have any logic inside your template and all logic is written inside your component. This is extremely easy to support. If you start to pack a lot of logic inside your templates, they will really fast become unsupportable.

If else conditions

It is also possible to write if else condition directly in template. But actually I would say that in production not a lot of people use it because it is not that comfortable.

<div *ngIf="isOpened; else noTemplate">Fooo</div>

<ng-template #noTemplate>
  We don't have fooo
</ng-template>

The idea here is to provide a else condition which will render a template. It is important to put a #noTemplate in our ng-template because it is a reference to what needs to be rendered in else case.

As you can see in browser here is our foo, because our property isOpened is set in true. If we try to set it to false, then in browser we render "We don’t have foo".

Why it is not comfortable? We create here ng-template and not just a div and we must provide this template inside else. This is why not a lot of people are writing code like this. Typically you will see something like this.

<div *ngIf="isOpened">Fooo</div>
<div *ngIf="!isOpened">We don't hve foo</div>

Here we just have 2 divs and we negate second condition. We don't write any templates and this is much easier to read.

Ng-container

We have also another possibility. Sometimes we don't want to create additional container like div, but we need to add ngIf condition. Instead of div we can use here ng-container. This is a special tag in Angular and it won't be rendered inside our DOM.

<ng-container *ngIf="isOpened">Fooo</ng-container>

Which actually means here we check isOpened and as you can see inside DOM we have just a string foo. We don’t have ng-container and we don't have here a div. If you have some strict css and you don't need a wrapper, then you can use ng-container and not div.

If else then

It might also happen that you want to write if else then in your template. This is completely possible.

<div *ngIf="isOpened; then fooTemplate; else noTemplate">Fooo</div>

<ng-template #fooTemplate>Fooo</ng-template>
<ng-template #noTemplate>We don't have fooo</ng-template>

Here we define 2 templates and render them depending on the condition. As you can see here first template is rendered because isOpened property is in true. If we set isOpened to false, then we render another template.

So, we have this approach, but it is more difficult. This is why most of the people just use ngIf some condition and ngIf with negated condition.

As keyword

And the last thing that you must know about ngIf is the as keyword. This is super important because this is the best way how you can write an async pipe inside Angular and how you can combine data for your template. What I am talking about at all?

<div *ngIf="foo$|async">
  {{(foo$ | async)?.title}}
  {{(foo$ | async)?.description}}
</div>

So we have a stream and we first check that the value from it is truthy to render the content. The we access the same stream again and again inside to render different properties. This is not a good approach and it makes our application much slower because Angular created more subscriptions.

Instead we can use as keyword.

<div *ngIf="foo$ | async as foo">
  {{foo.title}}
  {{foo.description}}
</div>

Here we used as keyword and we store the result of our stream in a local property foo which is available for us inside the block. Here we have just a single stream for the whole file and this is awesome. Actually this is the best variant how you can work with async pipes inside your template.

Now you know everything that you need to know about ngIf inside Angular. And actually if you are interested to see how inside Angular you can create nested comments, make sure to check this post also.