Communication between components – inputs and outputs – Components and Pages

In our gym diary application, we now need the workout list page component, DiaryComponent, to communicate with the list item component, EntryItemComponent.

The simplest way to accomplish this communication is with Angular’s Property Binding concept. Despite the complicated name, in practice, we annotate a component object’s property with the @Input annotation, so Angular creates a custom HTML attribute on the component.

Let’s see this concept in practice; first, let’s create an interface that will represent an item in our diary:
ng g interface diary/interfaces/exercise-set

With the preceding command, we create the file and, as an organized practice, we create a folder to store the module’s interfaces. In the generated file, we will define the object we want to communicate with:
export interface ExerciseSet {
  id?: string;
  date: Date;
  exercise: string;
  sets: number;
  reps: number;
}
export type ExerciseSetList = Array<Exerc
iseSet>;

We create an interface defining the object and a type to define a list of exercises, improving the future readability of our implementation.

Now, in the entry-item.component.ts file, let’s add the new property:
import { Component, Input } from ‘@angular/core’;
import { ExerciseSet } from ‘../interfaces/exercise-set’;
@Component({
 selector: ‘app-entry-item’,
 templateUrl: ‘./entry-item.component.html’,
 styleUrls: [‘./entry-item.component.css’]
})
export class EntryItemComponent {
  @Input(‘exercise-set’) exerciseSet!:ExerciseSet;
}

Here we create a property called exerciseSet of type ExerciseSet that we just defined. We use the ! symbol in the type definition because we are going to define its value at runtime.

The @Input annotation receives the exercise-set string as a parameter. With this, we define the name of the custom HTML attribute to be used in the template. This parameter is optional; if it’s not used, the name of the attribute will be the name of the property. Here, it would be exerciseSet.

Let’s now change our template to use this property:
<div class=”mb-4 border-b bg-white p-4″>
  <span class=”font-bold”>Date:</span> {{ exerciseSet.date | date }}<br />
  <span class=”font-bold”>Exercise:</span> {{ exerciseSet.exercise }}<br />
  <span class=”font-bold”>Sets:</span> {{ exerciseSet.sets }}<br />
  <span class=”font-bold”>Reps:</span> {{ exerciseSet.reps }}
</div>

To use the component’s properties inside the template, we use the {{ }} syntax. Here, we can see an advantage of using VS Code with the Angular Language Service extension enabled because we have type-checking in the HTML template, avoiding typos, for example.

Something to highlight in this example is the Date attribute. Here, we are using an Angular feature called pipe, which allows the formatting of a template element. In this case, we are formatting a date.

Let’s now configure a list of exercises in the diary.component.ts file:
import { Component } from ‘@angular/core’;
import { ExerciseSetList } from ‘../interfaces/exercise-set’;
@Component({
 templateUrl: ‘./diary.component.html’,
 styleUrls: [‘./diary.component.css’],
})
export class DiaryComponent {
  exerciseList: ExerciseSetList = [
    { id: ‘1’, date: new Date(), exercise: ‘Deadlift’, reps: 15, sets: 3 },
    { id: ‘2’, date: new Date(), exercise: ‘Squat’, reps: 15, sets: 3 },
    { id: ‘3’, date: new Date(), exercise: ‘Barbell row’, reps: 15, sets:
 3 },
 ];
}

For this example, we create a property called exerciseListExample and fill it with objects from the ExerciseSet interface. Now, let’s change the list template in the diary.component.html file:
  <section class=”mb-8″>
    <h2 class=”mb-4 text-xl font-bold”>List of entries</h2>
      <ul class=”rounded border shadow”>
        <li *ngFor=”let item of exerciseList”>
          <app-entry-item [exercise-set]=”item” />
        </li>
      </ul>
  </section>

In the template, we are using the ngFor directive, which has the function of iterating over a list and rendering the element we want to define in the template. For each list item, we are going to create a new app-entry-item component and now we want to assign an item to it.

To do that, we use the [exercise-set] attribute to pass the item provided by ngFor. When we run our project, we have the list, as shown in the following figure:

Figure 4.2 – Gym diary application UI after refactoring

With this, we understand how to pass information from one component to another, but we can improve this project by introducing a good performance practice, the TrackBy property.

Write a Comment

Your email address will not be published. Required fields are marked *