Emitting data through custom events

Now that we know how to emit custom events from our component API, why don't we take a step further and send data signals beyond the scope of the component? We already discussed that the emit() event of the EventEmitter<T> class accepts in its signature any given data of the type represented by the T annotation. Let's extend our example to notify the progress of the countdown. Why would we ever want to do this? Basically, our component displays onscreen a visual countdown, but we might want to watch the countdown progress programmatically in order to take action once the countdown is finished or reaches a certain point.

Let's update our timer component with another output property that matches
the original and emits a custom event on each iteration of the seconds property,
as follows:

class CountdownTimerComponent {
@Input() seconds: number;
@Output() complete: EventEmitter<any> = new EventEmitter();
@Output() progress: EventEmitter<number> = new EventEmitter();
intervalId;

constructor() {
this.intervalId = setInterval(() => this.tick(), 1000);
}

private tick(): void {
if(--this.seconds < 1) {
clearTimeout(this.intervalId);
this.complete.emit(null);
}
this.progress.emit(this.seconds);
}
}

Now, let's rebuild our host component's template to reflect the actual progress of the countdown. We already do so by displaying the countdown, but that is a feature handled internally by the CountdownTimerComponent. Now, we will keep track of the countdown outside this component:

@Component({
selector: 'timer',
template: `
<div class="container text-center">
<countdown-timer [seconds]="25"
(progress)="timeout = $event"
(complete)="onCountdownCompleted()" >
</countdown-timer>
<p *ngIf="timeout < 10">
Beware! Only
<strong>{{ timeout }} seconds</strong>
</p>
</div>`
})
export class TimerComponent {
timeout: number;
onCountdownCompleted(): void {
alert('Time up')
}
}

We took advantage of this round of changes to formalize the timeout value as a property of the host component. This allows us to bind new values to that property in our custom event handlers, as we did in the preceding example. Rather than binding an event handler method to the (progress) handler, we refer to the $event reserved variable. It is a pointer to the payload of the progress output property that reflects the value we pass to the emit() function when executing this.progress.emit(this.seconds). In short, $event is the value assumed by this.seconds inside CountdownTimerComponent. By assigning such a value to the timeout class property within the template, we are also updating the binding expressed in the paragraph we just inserted into the template. This paragraph will only become visible when timeout is lower than 10:

<countdown-timer [seconds]="25"
(progress)="timeout = $event"
(complete)="onCountdownCompleted()">
</countdown-timer>

What we saw in this section was how we could send data from the component to the container. There are essentially two ways of doing that:

  • Assign $event to the container property
  • Invoke the container method with $event as the function parameter

The first version is what we demonstrated, that is:

<countdown [seconds]="25" (progress)="timeout = $event" >
</countdown>

With the component invoking it as follows:

progress.emit(data);

The second version is a small rewrite of the preceding example:

<countdown [seconds]="25" (progress)="onProgress($event)">
</countdown>

We would invoke it the same way in the component, but the difference would be that we need to declare a container method, onProgress, so the timeout property gets set that way instead:

onProgress(data) {
this.timeout = data;
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset