Lesson Tuesday

In previous lessons we learned about basic Angular 2 directives, including the *ngFor repeater directive that allows us to loop through content in our templates and the ngModel directive that provides a shortcut for two-way data binding in forms.

In this lesson, we'll explore another common Angular 2 directive: ngIf. This directive allows us to embed conditional statements directly inside our templates. We'll use it to dynamically hide and show content in our To Do List.

ngIf

*ngIf is another built-in Angular directive. Like *ngFor, it is a structural directive because it has the power to alter the structure of a view. It looks something like this:

<div *ngIf="property">Content!</div>

As you can see, it's inserted directly into an HTML element just like other directives we've covered so far. It includes a property in quotation marks on the right side of the =. This property is generally a variable defined on the back-end of the component in which the directive resides.

If the property evaluates to true (the property exists and is not null), the HTML element the directive is bound to will be displayed to the user.

If it evaluates to false (the property does not exist or if its value is null), the HTML element it is bound to will not be rendered in the template.

ngIf in To Do

Our To Do List currently always displays an edit form. We can click the "Edit" button of another Task to view a different edit form, but this isn't the most ideal design for our user interface. Sometimes users may simply want to see their Tasks without being prompted to make any changes.

Let's update our template so the edit form isn't displayed when the page first loads. Instead, we'll show our users the edit form when they click a Task's "Edit" button. We'll also add a button to the form that allows the user to hide the edit form again once changes have been made.

First, let's redefine our selectedTask property. Instead of defaulting to the first item in our tasks array, let's set it to null. We'll remove this line: selectedTask: Task = this.tasks[0]; and replace it with the following:

app/app.component.ts
...
export class AppComponent {
  ...
  selectedTask = null;
...

This will ensure that when the page first loads, we won't see an edit form or detail area, only our main list of tasks. (Note that the updated code won't work yet because we still need to add our directive.)

Next, let's add a button to the bottom of the form to hide it entirely:

app/app.component.ts
...
    <div class="container">
      <h1>To Do List for {{month}}/{{day}}/{{year}}</h1>
      <h3>{{currentFocus}}</h3>
      <ul>
        <li [class]="priorityColor(currentTask)" (click)="isDone(currentTask)" *ngFor="let currentTask of tasks">{{currentTask.description}} <button (click)="editTask(currentTask)">Edit!</button></li>
      </ul>
      <hr>
      <div>
        <h3>{{selectedTask.description}}</h3>
        <p>Task Complete? {{selectedTask.done}}</p>
      </div>
      <div>
        <h3>Edit Task</h3>
        <label>Enter Task Description:</label>
        <input [(ngModel)]="selectedTask.description">
        <label>Enter Task Priority (1-3):</label>
        <br>
        <input type="radio" [(ngModel)]="selectedTask.priority" [value]="1">1 (Low Priority)<br>
        <input type="radio" [(ngModel)]="selectedTask.priority" [value]="2">2 (Medium Priority)<br>
        <input type="radio" [(ngModel)]="selectedTask.priority" [value]="3">3 (High Priority)
        <button (click)="finishedEditing()">Done</button>
      </div>
    </div>
...

As you can see, we've added an event binding that will listen for a click. When the button registers a click event, it will run a method called finishedEditing(). We'll define this method now:

app/app.component.ts
...
export class AppComponent {
  ...


  finishedEditing() {
    this.selectedTask = null;
  }

}
...

Finally, we'll add our *ngIf directive. Until the user specifically opts to edit a Task, the form should be hidden. We'll bind our *ngIf directive into the <div> where our detail view and form fields reside:

app/app.component.ts
...
    <div class="container">
      <h1>To Do List for {{month}}/{{day}}/{{year}}</h1>
      <h3>{{currentFocus}}</h3>
      <ul>
        <li [class]="priorityColor(currentTask)" (click)="isDone(currentTask)" *ngFor="let currentTask of tasks">{{currentTask.description}} <button (click)="editTask(currentTask)">Edit!</button></li>
      </ul>
      <hr>
      <div>
        <div *ngIf="selectedTask">
          <h3>{{selectedTask.description}}</h3>
          <p>Task Complete? {{selectedTask.done}}</p>
          <hr>
          <h3>Edit Task</h3>
          <label>Enter Task Description:</label>
          <input [(ngModel)]="selectedTask.description">
          <label>Enter Task Priority (1-3):</label><br>
          <input type="radio" [(ngModel)]="selectedTask.priority" [value]="1">1 (Low Priority)<br>
          <input type="radio" [(ngModel)]="selectedTask.priority" [value]="2">2 (Medium Priority)<br>
          <input type="radio" [(ngModel)]="selectedTask.priority" [value]="3">3 (High Priority)
          <button (click)="finishedEditing()">Done</button>
        </div>
      </div>
    </div>
...

Updating the user interface of an Angular application by toggling properties is very common.

Let's do a quick recap. When we load the page, we don't see our edit form because selectedTask is set to null. When we click the edit button, it triggers our editTask method and sets the value of the selectedTask to clickedTask. Now that selectedTask is no longer null, our *ngIf directive evaluates to true and our form shows. We can then hide the form again by triggering our finishedEditing() method, which returns the value of the selectedTask to null.

As you can see, *ngIf offers some pretty useful functionality! By dynamically hiding and showing different areas of your application, you can ensure users are only receiving the information relevant to them.

Try using the *ngIf directive in future projects to add even more interactivity into your application's templates.


Example GitHub Repo for Angular 2 To Do List