Angular *ngIf Offers Much More Than Just An IF Condition

You might feel that there are plenty of articles around which discuss about *ngIf directive in angular - why another article?. The reason I wanted to write this article is :

Many of the articles discuss how to add/remove an element based on the IF condition and do not explain how to write ELSE part and how PIPES can be used with *ngIf.

Let's take one example scenario and see  different ways to implement it

Scenario

  • Display 'Login' and 'Signup' buttons when user is not logged in
  • Display 'Hello user' message and a  'Logout' button when user is already logged in

Approach #1:  Using only IF condition

<div *ngIf="isLoggedIn">    <!-- If user is logged in -->
  <label class="mr-2">Hello, User!</label>
  <button class="btn btn-primary" (click)="isLoggedIn = !isLoggedIn">
    Logout
  </button>
</div>
<div *ngIf="!isLoggedIn">  <!-- If user is NOT logged in -->
 <button class="btn btn-default mr-2" (click)="isLoggedIn = !isLoggedIn">
  Login
</button>
<button class="btn btn-primary">Signup</button>
</div>

As you can see, we are using just two IF conditions ( *ngIf=isLoggedIn   and *ngIf=!isLoggedIn ) here.

Approach #2:  Using  IF / ELSE condition

<div *ngIf="isLoggedIn; else loggedOut">  <!-- If NOT logged in use #loggedOut template-->
<label class="mr-2">Hello, User!</label>
<button class="btn btn-primary" (click)="isLoggedIn = !isLoggedIn">
  Logout
</button>
</div>
<ng-template #loggedOut>     <!-- loggedOut template -->
  <div>
  <button class="btn btn-default mr-2" (click)="isLoggedIn = !isLoggedIn">
  Login
</button>
<button class="btn btn-primary">Signup</button>
 </div>
</ng-template>

So, in the above case, we are using an IF / ELSE condition.  If the user is logged in, the div content will be shown, else it will show the content of ng-template ( identified by template reference -  #loggedOut  )

Approach #3:  Using  IF THEN / ELSE condition

<div *ngIf="isLoggedIn; then loggedIn; else loggedOut"></div>   <!-- If logged in, then use loggedIn template, else use loggedOut template -->

<ng-template #loggedIn>  <!-- loggedIn template-->
  <div>
 <label class="mr-2">Hello, User!</label>
<button class="btn btn-primary" (click)="isLoggedIn = !isLoggedIn">
  Logout
</button>  
  </div>
</ng-template>

<ng-template #loggedOut>  <!-- loggedOut template -->
  <div>
    <button class="btn btn-default mr-2" (click)="isLoggedIn = !isLoggedIn">
      Login
    </button>
    <button class="btn btn-primary">Signup</button>
  </div>
</ng-template>

In the above case, we have created two templates ( one for login and the other for logout ), which looks pretty clean compared to other two approaches

There is NO performance benefit of using one approach over the other. It is just that the code looks clean and more readable when you use apporach #3  in case you want to implement IF/ ELSE.

Before we wrap-up, let's see how we can use *ngIf with Pipes.

Scenario :

Display Bitcoin price with the ability to refresh

Implementation :

  • We will make a HTTP request to one of the public APIs available ( https://api.coindesk.com/v1/bpi/currentprice.json )  to get the bitcoin price.
  • We will show 'Please wait...' message till we get the response
  • Once we get the response, we will remove the message and show the bitcoin price
  • Please note that am using the above api just for DEMO purpose here - not sure whether that can be used for real-time purposes. Please do a research before you use it for any other purposes. More free APIs can be found here : https://apipheny.io/free-api/
import { delay, Observable } from 'rxjs';

@Component({
  selector: 'ng-if',
  templateUrl: './ngif.component.html',
  styleUrls: ['./ngif.component.css'],
})
export class NgIfComponent {
  isLoggedIn = false;
  bitcoinData: Observable<any>;

  constructor(public http: HttpClient) {
    this.getBitcoinData();
  }

  getBitcoinData() {
    // Below API is used for DEMO purpose. Please do research before you use it for any other purposes.
    this.bitcoinData = this.http
      .get('https://api.coindesk.com/v1/bpi/currentprice.json')
      .pipe(delay(3000));
  }
}
<div id="bitCoinCont">
  <span *ngIf="bitcoinData | async as data; else loading"> <!-- the async pipe here ensures that the content of the span element will be rendered only after bitcoin data is fetched -->
    {{ data['bpi']['USD']['rate'] }}
  </span>
  <button class="btn btn-primary" (click)="getBitcoinData()">Refresh</button>

  <ng-template #loading>Please wait...</ng-template> <!-- Loading template - displays Please wait.. -->
</div>

Please notice the *ngIf directive above. The async pipe subscribes to the bitcoinData oservable and once the data is available, it will be stored in data. And we are using the properties of data inside span element. The async pipe with *ngIf here ensures that the content of span will be rendered only when the data is available in data variable. Till then, it loads the #loading template.

Source code here

Your comments are always welcome :)