Building  Large-Scale Web Applications with Angular
上QQ阅读APP看书,第一时间看更新

Adding the next exercise indicator using ngIf

It will be nice for the user to be told what the next exercise is during the short rest period between exercises. This will help them prepare for the next exercise. So let's add it.

To implement this feature, we can simply output the title of the next exercise from the workoutPlan.exercises array. We show the title next to the Time Remaining countdown section.

Change the workout div (class="exercise-pane") to include the highlighted content, and remove existing Time Remaining h1:

<div class="exercise-pane"> 
<!-- Exiting html --> 
   <div class="progress time-progress"> 
       <!-- Exiting html --> 
   </div> 
    <div class="row">
<h4 class="col-sm-6 text-left">Time Remaining:
<strong>{{currentExercise.duration-exerciseRunningDuration}}</strong>
</h4>
<h4 class="col-sm-6 text-right" *ngIf="currentExercise.exercise.name=='rest'">Next up:
<strong>{{workoutPlan.exercises[currentExerciseIndex + 1].exercise.title}}</strong>
</h4>
</div> </div>

We wrap the existing Time Remaining h1 and add another h3 tag to show the next exercise inside a new div with some style updates. Also, there is a new directive, ngIf, in the second h3. The * prefix implies that it belongs to the same set of directives that ngFor belongs: structural directives. Let's talk a bit about ngIf.

The ngIf directive is used to add or remove a specific section of the DOM based on whether the expression provided to it returns true or false. The DOM element is added when the expression evaluates to true and is destroyed otherwise. Isolate the ngIf declaration from the preceding view:

ngIf="currentExercise.details.name=='rest'" 

The directive expression checks whether we are currently in the rest phase and accordingly shows or hides the linked h3.

Also in the same h3, we have an interpolation that shows the name of the exercise from the workoutPlan.exercises array.

A word of caution here: ngIf adds and destroys the DOM element, and hence it is not similar to the visibility constructs that we employed to show and hide elements. While the end result of style, display:none is the same as that of ngIf, the mechanism is entirely different:

<div [style.display]="isAdmin" ? 'block' : 'none'">Welcome Admin</div> 

Versus this line:

<div *ngIf="isAdmin" ? 'block' : 'none'">Welcome Admin</div> 

With ngIf, whenever the expression changes from false to true, a complete re-initialization of the content occurs. Recursively, new elements/components are created and data binding is set up, starting from the parent down to the children. The reverse happens when the expression changes from true to false: all of this is destroyed. Therefore, using ngIf can sometimes become an expensive operation if it wraps a large chunk of content and the expression attached to it changes very often. But otherwise, wrapping a view in ngIf is more performant than using CSS/style-based show or hide, as neither the DOM is created nor the data binding expressions are set up when the ngIf expression evaluates to false.

New version of Angular support branching constructs too. This allows us to implement the if then else flow in the view HTML. The following sample has been lifted directly from the platform documentation of ngIf:

<div *ngIf="show; else elseBlock">Text to show</div>
<ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>

The else binding points to a ng-template with template variable #elseBlock.

There is another directive that belongs in this league: ngSwitch. When defined on the parent HTML, it can swap the child HTML elements based on the ngSwitch expression. Consider this example:

<div id="parent" [ngSwitch] ="userType"> 
<div *ngSwitchCase="'admin'">I am the Admin!</div> 
<div *ngSwitchCase="'powerUser'">I am the Power User!</div> 
<div *ngSwitchDefault>I am a normal user!</div> 
</div> 

We bind the userType expression to ngSwitch. Based on the value of userType (admin, powerUser, or any other userType), one of the inner div elements will be rendered. The ngSwitchDefault directive is a wildcard match/fallback match, and it gets rendered when userType is neither admin nor powerUser.

If you have not realized it yet, note that there are three directives working together here to achieve switch-case-like behavior:

  • ngSwitch
  • ngSwitchCase
  • ngSwitchDefault

Coming back to our next exercise implementation, we are ready to verify the implementation, start the app, and wait for the rest period. There should be a mention of the next exercise during the rest phase, as shown here:

The app is shaping up well. If you have used the app and done some physical workouts along with it, you will be missing the exercise pause functionality badly. The workout just does not stop until it reaches the end. We need to fix this behavior.