Understand Angular EventEmitter, @Input, @Output in minutes with example
Tags: angular, tutorial, beginners, webdev
Published: 2020-08-30 14:44:52

Let's get to the point. Angular document gives me a basic understanding of each feature, but when it come to solving real world problem, I would be thinking of _how do they work together?_.

I've written one example to demonstrate just that.

## Problem Scenario

In one course, I have to learn two subjects. Angular and Code Sandbox. In Angular subject I have two tasks which give me two credit. Code sandbox has one task with one credit.
To complete the course, I need to gain three credits.

**Now I need to write an application to track my progress.**

### Big Picture

```text
+--------------------+
| Subjects           | : Keeping record of credits
|  +--------------+  |
|  | Tasks        |  | : To do list, tick off done tasks
|  | +---------+  |  |
|  | | Report  |  |  | : Keeping record of task completed
|  | +---------+  |  |
|  +--------------+  |
+--------------------+
```

### Abstract Steps

1. Initialise Default Angular Project
2. Create three components (subjects, tasks, report)
3. Replace content of `app.comopnent.html` with `subjects` component
4. Add functions

<!-- ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oglshzcu9a15hg55vjgu.png) -->

### Codes

This diagram let you visualise where `@Input`, `@Output` and `EventEmitter` is used

```text
+----------+  Data Flow  +-----------+
|  Tasks   + ----------> + Subjects  |
+----------+             +-----------+
@Output():EventEmitter

+----------+  Data Flow  +----------+
|  Tasks   + ----------> +  Report  |
+----------+             +----------+
                         @Input():number
```

#### Subject Component

```html
<section id="subjects">
  <h4>Subjects</h4>
  <div *ngFor="let subject of subjects">
    <div>{{subject.desc}} - Credit: {{subject.credit}}</div>
  </div>
  <app-task
    (subjectCompleteEvent)="receiveSubjectCompletion($event)"
  ></app-task>
</section>
```

```typescript
import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-subject",
  templateUrl: "./subject.component.html"
})
export class SubjectComponent implements OnInit {
  subjects: any[] = [];

  ngOnInit(): void {
    this.subjects.push({ code: "ng", desc: "Angular", credit: 0 });
    this.subjects.push({ code: "sb", desc: "Code Sanbox", credit: 0 });
  }

  receiveSubjectCompletion(subjectComplete: any) {
    const index = this.subjects
      .map((subject) => {
        return subject.code;
      })
      .indexOf(subjectComplete.code);

    subjectComplete.credit
      ? this.subjects[index].credit++
      : this.subjects[index].credit--;
  }
}
```

#### Tasks Component

```html
<section id="tasks">
  <h4>Tasks</h4>
  <div *ngFor="let task of todos">
    <input
      type="checkbox"
      (change)="onCheck($event)"
      value="{{task.code}}"
    />{{task.desc}}
  </div>
  <app-report [completed]="taskCompleteCounts"></app-report>
</section>
```

```typescript
import { Component, OnInit, Output, EventEmitter } from "@angular/core";

@Component({
  selector: "app-task",
  templateUrl: "./task.component.html"
})
export class TaskComponent implements OnInit {
  // define output type for notifying parent component
  @Output() subjectCompleteEvent: EventEmitter<any> = new EventEmitter<any>();
  // input value for report (child)
  taskCompleteCounts = 0;

  todos: any[] = [];
  ngOnInit(): void {
    this.todos.push({ code: "ng", desc: "Scaffold Angular Project" });
    this.todos.push({ code: "ng", desc: "Try EventEmitter, @Input, @Output" });
    this.todos.push({ code: "sb", desc: "Try Code Sandbox" });
  }

  onCheck(event: any) {
    if (event.target.checked) {
      // update value for child
      this.taskCompleteCounts++;
      // notify parent
      this.subjectCompleteEvent.emit({
        code: event.target.value,
        credit: true
      });
    } else {
      this.taskCompleteCounts--;
      this.subjectCompleteEvent.emit({
        code: event.target.value,
        credit: false
      });
    }
  }
}
```

#### Report Component

```html
<section id="report">
  <h4>Report</h4>
  <div>
    Task completed: {{completed}}
  </div>
</section>
```

```typescript
import { Component, OnInit, Input } from "@angular/core";

@Component({
  selector: "app-report",
  templateUrl: "./report.component.html"
})
export class ReportComponent implements OnInit {
  // define input type. value will be updated from parent
  @Input() completed: number;
  ngOnInit(): void {}
}
```

#### Demo

https://codesandbox.io/s/ng-eventemitter-input-output-57r1z

------------

It's freedom to use this content under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/), program codes under [CC0](http://creativecommons.org/publicdomain/zero/1.0/) licence.