DEV Community

NETIC
NETIC

Posted on

Understand Angular EventEmitter, @Input, @Output in minutes with example

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

+--------------------+
| 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

Codes

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

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

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

Subject Component

<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>
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

<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>
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

<section id="report">
  <h4>Report</h4>
  <div>
    Task completed: {{completed}}
  </div>
</section>
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, program codes under CC0 licence.

Top comments (0)