Communication from the child component – using @Output – Components and Pages
We studied how parent components, which can be either smart or presentational, can communicate with their child components by using attributes marked with the @Input decorator.
However, when we need the opposite, the child component passes some information to the parent. As we saw in the previous section, business rule processing should ideally happen in the Smart component. For this type of communication, we mark attributes with the @Output decorator.
Let’s create a button for adding an item to our diary. We’ll see the use of forms in Chapter 6, Handling User Input: Forms, but here we want to focus on the interaction between components.
Using the Angular CLI, we will create the new component using this command:
ng g c diary/new-item-button
In the new component’s template, let’s move the diary button template into the component:
Add new entry
In the new-item-button.component.ts file, we will add the new attribute:
import { Component, EventEmitter, Output } from ‘@angular/core’;
import { ExerciseSet } from ‘../interfaces/exercise-set’;
@Component({
selector: ‘app-new-item-button’,
templateUrl: ‘./new-item-button.component.html’,
styleUrls: [‘./new-item-button.component.css’],
})
export class NewItemButtonComponent {
@Output() newExerciseEvent = new EventEmitter();
addNewExercise() {
const id = Date.now().toString();
const date = new Date();
const reps = 10;
const sets = 4;
const exercise = ‘Leg Press’;
const newExerciseSet: ExerciseSet = { id, date, reps, sets, exercise };
this.newExerciseEvent.emit(newExerciseSet);
}
}
Here, we first create the newExerciseEvent attribute and add the @Output decorator to define that it will be an attribute present in the component’s template.
Here, there is a difference from the @Input attribute; in this case, we are already assigning an object of the EventEmitter class to the variable. This Angular class aims to emit events when a certain action takes place.
This is necessary because, unlike @Input, the value of which is assigned when the component is structured and rendered, @Output communication can occur at any time, depending on the user’s action.
The EventEmitter class uses TypeScript’s type-checking capability, making it possible for us to determine what type of object we are going to emit to the parent component.
In the addNewExercise method, we create an object of type ExerciseSet, and using the emit method of the EventEmitter class, we pass this object to the parent component.
Back to the template – let’s add the method call to the button’s click action:
Add new entry
Now let’s refactor DiaryComponent to consume the new button:
.
.
.
Server Sync
.
.
.
In the template, we are using the app-new-item-button component to pass the addExercise function to the newExerciseEvent attribute.
Here, we can highlight that the binding of an @Output attribute must be done with parentheses – ( ) – and this $event parameter represents the object that the child component will emit. If you highlight this parameter in VS Code, we can verify that it is of type ExerciseSet.
Finally, let’s create the addExercise method in the component:
.
.
.
addExercise(newSet: ExerciseSet) {
this.exerciseList.push(newSet);
}
.
.
.
Our method receives the emitted value and adds it to the exercises array. Running our project, we can see that the items are successfully added.
In this example, we can see in practice the whole flow of the design pattern of the Smart and presentation components. When clicking on the Add Exercises button, the Diary Smart component receives the new exercise from the NewItemButtonComponent presentation component.
By updating the list, the list is automatically passed to the ListEntriesComponent component, which renders the list on the screen. Now we are going to implement actions for the items of the list of exercises – we will see how to emit events of these items and how to identify these elements.