Angular Decorators

How to Pass Data Between Components in Angular 8

Parent-Children-Sibling Relationships

mike dietz
Webtips

--

Photo by Daniel Cheung on Unsplash

Angular, like any of the other major frontend frameworks, uses components as their central building blocks. The advantages of separation into logical units, especially with large applications, are great: code becomes more manageable, it improves readability, reusability, and maintainability.

With a component-based architecture, sharing data between components becomes a topic. Angular offers a complete toolset for inter-component communication.

How to pass data from one component into another one depends on their relationship with each other. The concept of parent-child-sibling relationship, which is essential when working with HTML, CSS and the DOM, applies also to components in Angular, or any other frontend framework. We distinguish between four different types of relationships:

  • Parent to Child
  • Child to Parent
  • Child to Child (Siblings)
  • Non-Related (no direct relationship)

For communication between non-related components, services are used. Services can be used for many purposes and are essential in Angular, therefore, they deserve more attention than we could give them in this context. This article deals with the basic approaches for the first three relationship types only. Each one of them requires a different approach and Angular utilizes Typescript’s decorators for this task.

Decorators are central in Angular. There are many types, for many purposes and they deserve undivided attention, i.e. an article for themselves. But in a nutshell, a decorator is a function that stores metadata. (Metadata is descriptive data about other data. For example, a book’s metadata contains the author’s name, the category, the publisher, etc.) In our case, the metadata contains a set of properties that ‘decorates’ a class, method, property, or parameter and defines its functionality. For our purpose, we utilize some property decorators. A decorator can be recognized by the preceding @ symbol, e.g. @Input(), @Output, @ViewChild.

Each communication type is explained step-by-step, individual stackblitz’ are available.

Parent to Child

@Input() Decorator
Stackblitz: Parent-Child @Input()

This example uses [(ngModel)] double-binding which automatically updates the relating field in the Child1 component.

in the parent component:

  1. Create a variable that stores the data.
    currentMsgToChild1 = ‘’;
  2. Use double-binding with [(ngModel) ]for the newly created variable.
    <input type=”text” [(ngModel)]=”currentMsgToChild1">
  3. Add the child template and use property binding to assign the variable to the property.
    <app-child1 [msgFromParent1]=’currentMsgToChild1'></app-child1>

in the child component:

  1. Declare a variable with the @Input decorator.
    @Input() msgFromParent1: any[];
  2. Render the data in the template.
    <p>{{ msgFromParent1 }}</p>

The Reset button is only added to clear the entry field.

Child to Parent

I demonstrate two ways of passing the data from the child to the parent.

screenshot template ViewChild()

ViewChild()
Stackblitz: Child-Parent ViewChild()
ViewChild() is a property decorator that updates the property if a DOM view change occurs. It gives the parent access to all the attributes and functions of the child component.

Prerequisites:
For ViewChild() to work, the lifecycle-hook ngAfterViewInit() needs to be implemented and the child component needs to be imported into the parent component.

in the parent component:

  1. Import ViewChild() and ngAfterViewInit() and implement the lifecycle hook.
    import { ViewChild, AfterViewInit } from ‘@angular/core’;
    export class AppComponent implements AfterViewInit {…}
    ngAfterViewInit() {}
  2. Import the child component.
    import { Child1Component } from ‘./child1/child1.component’;
  3. Use the @ViewChild() directive.
    @ViewChild(Child1Component, {static: false}) child1: Child1Component;
  4. Declare a variable that holds the data.
    msgFromChild1: any;
  5. In ngAfterViewInit() call the variable that holds the data.
    ngAfterViewInit() {this.msgFromChild1 = this.child1.msgFromChild1; }

in the child component:

  1. Create two variables, one that holds the data via double-binding and one that is an array.
    currentMsgToParent=’’;
    msgFromChild1 = [];
  2. Create an input with double-binding for the newly created variable currentMsgToParent.
    <input type=”text” [(ngModel)]=”currentMsgToParent”>
  3. Use event binding to fire the function.
    <button (click)=”msgToParent()”>send</button>
  4. Create the function that pushes the value of the variable into the array.|
    msgToParent() { this.msgFromChild1.push(this.currentMsgToParent); }

@Output() decorator and EventEmitter()
stackblitz: Child-Parent Output() and EventEmitter()

screenshot template Output() and EventEmitter()

In child2 component:

  1. Declare a variable that is assigned to the @Ouput decorator which is set to a new EventEmitter().
    @Output() callParent = new EventEmitter();
  2. Create a variable that holds the data.
    currentMsgToParent = ‘cry, cry…’;
  3. Create a function that calls emit on the event with the data that we want to send.
    msgToParent() {this.callParent.emit(this.currentMsgToParent);}
  4. In the template create a button with a click event that calls the function. <button (click)=”msgToParent()”>Cry</button>

In the parent component:

  1. Create a variable to hold the data.
    msg: string;
  2. Create a function to receive the data, use $event as a parameter, and set it equal to the data variable.
    getMsgFromBaby($event) {this.msg = $event;}
  3. In the template use property binding to run the function whenever the event occurs.
    <app-baby (callParent)=”getMsgFromBaby($event)”></app-baby>

Sibling to Sibling

@Output(), EventEmitter() and Input
Stackblitz: Sibling Output-Input
Passing data between siblings uses a combination of the aforementioned decorators. Child1 passes data to Parent with @Output() and EventEmitter() and the Parent will pass the data to the receiving sibling, Child2, via Input().

screenshot template sibling relationship

in the child1 component:

  1. Create a variable that holds the data.
    currentMsgToSibling = ‘’;
  2. Create an input field that uses double-binding for the newly created variable.
    <input type=”text” [(ngModel)]=”currentMsgToSibling”>
  3. Import Output and EventEmitter into the class.
    import { Component, Input, Output, EventEmitter } from ‘@angular/core’;
  4. Declare a variable that is assigned to the @Output() decorator and set it to a new EventEmitter().
    @Output() msgToSibling = new EventEmitter<any>();
  5. Create a function that calls emit with the data on the event.
    msgToSib() { this.msgToSibling.emit(this.currentMsgToSibling)}
  6. Create a button with an event handler to fire the newly created function. <button (click)=”msgToSib()”>send</button>

in the parent component:

  1. Create a variable that holds the data received from Child1.
    currentMsgFromChild1ToChild2 : any;
  2. Create a function that assigns the send data to the newly created variable. fwdMsgToSib2($event) { this.currentMsgFromChild1ToChild2 = $event; }
  3. In the child1 selector, the parent can now subscribe to the msgToSibling event, which is outputted by the child component, and run the fwdMsgToSib2($event) function whenever this event occurs.
    <app-child1 (msgToSibling)=”fwdMsgToSib2($event)”> </app-child1>

in the child2 component:

  1. Import @Input() into the component.
    import { Component, Input } from ‘@angular/core’;
  2. Assign the parent variable currentMsgFromChild1ToChild2 to the @Input() decorator.
    @Input() currentMsgFromChild1ToChild2: any [];
  3. Use interpolation to render the variable.
  4. <p>{{ currentMsgFromChild1ToChild2 }}</p>

Summary:

  • Parent to Child — @Input() with property binding
    currentMsgToChild1 = ‘’;
    <input type=”text” [(ngModel)]=”currentMsgToChild1">

    <app-child1 [msgFromParent1]=’currentMsgToChild1'></app-child1>
    @Input() msgFromParent1: any[];

    <p>{{ msgFromParent1 }}</p>
  • Child to Parent — @Output() and EventEmitter() with event binding
    @Output() callParent = new EventEmitter();
    currentMsgToParent = ‘cry, cry…’;
    msgToParent() {this.callParent.emit(this.currentMsgToParent);} <button (click)=”msgToParent()”>Cry</button>
    msg: string;

    getMsgFromBaby($event) {this.msg = $event;}
    <app-baby (callParent)=”getMsgFromBaby($event)”></app-baby>
  • Child to Parent — @ViewChild
    @ViewChild(Child1Component, {static: false}) child1:Child1Component;
    msgFromChild1: any;
    ngAfterViewInit() {this.msgFromChild1 = this.child1.msgFromChild1; }
    currentMsgToParent=’’;
    msgFromChild1 = [];

    <input type=”text” [(ngModel)]=”currentMsgToParent”>
    <button (click)=”msgToParent()”>send</button>
    msgToParent() { this.msgFromChild1.push(this.currentMsgToParent); }
  • Child to Child (Siblings)combination of the above
  • Non-Related (no direct relationship) -use services
    * services can be used for any of the above relationships

Stackblitz: Variations on the Different Approaches

screenshot template communication between components

--

--