Lesson Monday

We began our Angular To Do List application in the last lesson. It can display the current focus of our list, today's date, and details for our first Task. However, our list will eventually need to display multiple Task objects.

So far we are manually inserting each new Task into the template like this:

app/app.component.ts
...
  template: `
    <div class="container">
     <h1>To Do List for {{month}}/{{day}}/{{year}}</h1>
     <h3>{{currentFocus}}</h3>
     <ul>
       <li>{{firstTask.description}}</li>
     </ul>
   </div>
  `
...

This isn't very efficient. Angular has a set of built-in tools called directives that allow us to add loops, conditionals, and other functionality directly into our templates.

This may sound like a new concept, but you've likely already added dynamic functionality directly to your templates in previous courses.

Java uses the Velocity templating system:

result.vtl
...
#foreach ($rectangle in $myRectangles)
  <p><h3>Your rectangle's length is $rectangle.getLength()</h3></p>
  <p><h3>Your rectangle's width is $rectangle.getWidth()</h3></p>
  #if ($rectangle.isSquare())
    <p><h3>Your rectangle is a square!</h3></p>
  #else
    <p><h3>Your rectangle isn't a square!</h3></p>
  #end
#end
...

PHP has conditionals and loops in Twig templates:

views/tasks.html.twig
...
{% if tasks is not empty %}
    <p>Here are all your tasks:</p>
{% endif %}
...

Ruby has dynamic loops and conditionals in Sinatra .erb templates:

<h1>Vehicles</h1>

<h3>Here are the vehicles on this lot: </h3>
<ul>
  <% @vehicles.each() do |vehicle| %>
    <li><a href="/vehicles/<%= vehicle.id() %>"><%= vehicle.model() %></a></li>
  <% end %>
</ul>

C# has similar functionality in Razor:

...
@foreach (var album in Model)
{
  <h4>@album.GetTitle()</h4>
  <p>By: @album.GetArtist()</p>
  <p>Price: @album.GetPrice()</p>
}
...

Introduction to Directives

In Angular, a directive is a function embedded directly into the template that can dynamically change the way the DOM appears. Much like a director directs the actors on the set of a movie, an Angular directive directs the appearance and structure of the DOM on the fly.

There are three types of directives in Angular:

  • Structural Directives: These change the structure of the DOM by adding and removing elements. (This is similar to how we added and removed elements using the jQuery .append() and .remove() methods in Intro to Programming).

  • Attribute Directives: These change the appearance or behavior of an element in the DOM (for instance, we can dynamically add or remove CSS styles).

  • Components: Components are directives, too. This is because they can be dynamically rendered in the DOM, and alter its appearance.

This lesson will focus solely on structural directives. We'll cover the other types of directives later on.

Structural Directives

The term structural directive may sound technical, but you likely already understand the basic concepts behind it.

When we learned basic JavaScript in Intro to Programming, we often wrote code that looked like this:

example-javascript-to-do-list.js
$(document).ready(function() {

    var tasks = ['Finish weekend Angular homework for Epicodus course', 'Begin brainstorming possible JavaScript group projects', 'Add README file to last few Angular repos on GitHub'];

    tasks.forEach(function(currentTask) {
       $(".to-do-list").append(currentTask);
    });

});

Pay close attention to the forEach() loop. The code above cycles through each entry in the tasks array, adding it to the area of the DOM with a to-do-list class. It uses jQuery's append() method to insert each item.

However, jQuery can be very finicky. We have to carefully refer to HTML elements in the jQuery, dynamically inserting, removing, or altering things from a separate file.

Thankfully, Angular directives offer comparable capabilities and also allow us to bind functionality directly to an HTML element, dynamically altering HTML elements in index.html and binding similar functionality directly onto our HTML elements.

The ngFor Repeater Directive

To explore how this works, we'll use a directive to display multiple Task objects in our To Do List.

First, we'll create another property in our root component's AppComponent class. We'll call it tasks and declare it will contain an array of Task objects. We'll also place our previously-existing Task in this array:

app/app.component.ts
...
export class AppComponent {
  currentFocus: string = 'Angular Homework';
  currentTime = new Date();
  month: number = this.currentTime.getMonth() + 1;
  day: number = this.currentTime.getDate();
  year: number = this.currentTime.getFullYear();
  tasks: Task[] = [
    new Task('Finish weekend Angular homework for Epicodus course'),
  ];
}
...

Next, let's create a few more Task objects in our new tasks array so we have a collection to loop through:

app/app.component.ts
...
export class AppComponent {
  currentFocus: string = 'Angular Homework';
  currentTime = new Date();
  month: number = this.currentTime.getMonth() + 1;
  day: number = this.currentTime.getDate();
  year: number = this.currentTime.getFullYear();
  tasks: Task[] = [
    new Task('Finish weekend Angular homework for Epicodus course'),
    new Task('Begin brainstorming possible JavaScript group projects'),
    new Task('Add README file to last few Angular repos on GitHub')
  ];
}
...

Let's display our Tasks in the template. We can loop through an array of objects using Angular's repeater directive like this:

app/app.component.ts
...

@Component({
  selector: 'app-root',
  template: `
    <div class="container">
     <h1>To Do List for {{month}}/{{day}}/{{year}}</h1>
     <h3>{{currentFocus}}</h3>

     <ul>
       <li *ngFor="let currentTask of tasks">{{currentTask.description}}</li>
     </ul>

   </div>
  `
})

...

Pay careful attention to the <li> tag. It now reads <li *ngFor="let currentTask of tasks">. This is our directive. Let's break down this new syntax:

  • *ngFor is Angular's repeater directive. By adding this directly to the <li> tag, we are establishing this <li> element as the repeater template. We're instructing Angular to make an additional copy of this <li> for each item in our list. We can make any element the repeater template simply by adding the directive to its tag.

  • "let currentTask of tasks" instructs Angular what array we're looping through (the tasks array) and what variable to assign to the array item the loop is currently on (currentTask, here). As we loop through the tasks array, each item in the array takes a turn at being the currentTask. (Also, the let statement is simply a new ES6 way to declare variables, as described here)

The code above is similar to a JavaScript forEach() loop but is embedded directly into our HTML using Angular syntax.

If we look in the browser, we have an <li> element in the DOM for each Task object in our tasks array.

angular-repeater-directive-in-action

Directives can be used to hide or show areas of the DOM using a conditional statement, dynamically add or remove CSS classes, and more. You can even define your own custom directives. As our applications grow in size, we'll learn more directives to manage dynamic, on-the-fly changes to our DOM.

For more information, check out the Angular Documentation for Structural Directives.


Example GitHub Repo for Angular 2 To Do List