Lesson Wednesday

Let's make sure our application follows the "Data Down, Actions Up" design principle we learned in the previous lesson. This will ensure our application follows best practices, and that our components can communicate with one another.

Moving Data Down

First, let's make sure our data is in our parent component so it can be sent down. Our list of Tasks should reside in the root AppComponent.

We temporarily moved our list of Tasks from AppComponent to TaskListComponent in the last lesson so our TaskListComponent could list our Tasks. Now we'll move it back into the root AppComponent so it can pass data down to its child components.

We'll also rename our tasks array masterTaskList to clarify that it's the primary "master" list at the top of our hierarchy. Here's our root component's class declaration now:

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();
  selectedTask = null;

  masterTaskList: Task[] = [
    new Task('Finish weekend Angular homework for Epicodus course', 3),
    new Task('Begin brainstorming possible JavaScript group projects', 2),
    new Task('Add README file to last few Angular repos on GitHub', 2)
  ];

  editTask(clickedTask) {
    this.selectedTask = clickedTask;
  }

  finishedEditing() {
    this.selectedTask = null;
  }

}

Receiving Data with @Inputs

Now we'll create an input in the child TaskListComponent. A child component can only receive data from its parent if it has an input available.

An input is like an inbox where the parent component passes data to its child. Without the input, the child component can't receive the data.

The TaskListComponent must have access to our Tasks to function so we'll create an input so it can receive data from its parent AppComponent.

To do this, we add Input to our list of imports at the top of the file:

task-list.component.ts
import { Component, Input } from '@angular/core';
...

Then we define the input using the @Input decorator. We'll create a new @Input called childTaskList and specify that it's an array of Task objects by using the Task[] data type:

app/task-list/task-list.component.ts
...
export class TaskListComponent {
  @Input() childTaskList: Task[];
...
}

Let's make one other change before we move on. Now that we have a childTaskList instead of tasks, we need to update the *ngFor directive in task-list.component.html to use the new variable childTaskList:

app/task-list/task-list.component.html
<ul>
  <li [class]="priorityColor(currentTask)" *ngFor="let currentTask of childTaskList">{{currentTask.description}} <button (click)="editTask(currentTask)">Edit!</button></li>
</ul>

Now that the child TaskListComponent is prepared to receive data, we must instruct the parent to send it. We can do this in the <app-task-list> tags directly:

app/app.component.html
...
<app-task-list [childTaskList]="masterTaskList"></app-task-list>
...

We use [ ] to signify an input. We pass the value of masterTaskList from the parent component into childTaskList. The name of the child component's @Input() is always on the left in [square brackets]. The data being passed from the parent component is on the right in "quotations".

If we refresh our page in the browser, we should see our Tasks displaying correctly.

Review

This is the "Data down" part of "Data down, actions up." It's an incredibly important part of the Angular framework, so let's review the process again:

  1. Model data resides in the parent component. In our application, masterTaskList: Task[] lives in the parent component.
  2. The child component needs to be ready to receive data from the parent so we add an @Input to the child component's class declaration. In our case, we did the following: @Input() childTaskList: Task[];.
  3. Finally, we need to get the parent and child components talking to each other via the child component's selector tag (which is placed in the parent component's HTML template). In our application, we assigned the value of the child's childTaskList to the parent's masterTaskList like this: <app-task-list [childTaskList]="masterTaskList"></app-task-list>. The variable name on the left must match the variable assigned in the child's @Input. The variable name on the right must match the variable that holds the data in the parent component.