Friday 7 June 2019

Understanding Angular Component Interaction with @Input and @Output decorators

We all know Angular application is all about components and passing data from parent component to child component and vice versa is a little bit confusing.

Today will see Angular's @Input and @Output decorators using which we can interact with components.

@Input properties as name tell it is used to receive input i.e. data values. is used to send value from parent to child.

@Output properties send data from up the hierarchy. Means we can pass data from child to parent by raising an event.



@Input:

Now let see an example:

We following parent and child components:

1. app.component.ts (parent component)
2. mycar.component.ts (child component)

- app.component.ts:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styles:[`.myapp{background:yellow; padding:50px;}`]
})
export class AppComponent  {
  latestCar:string = "BMW";
}

Here my app component has a string containing name of latest car. i.e. latestCar and value is "BMW".

Now lets create the mycar component.

- mycar.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'my-car',
  templateUrl: './mycar.component.html',
  styles:[`.mycar{background:blue; color:white; padding:20px; margin-top:40px;}`]
})
export class MyCarComponent  {
  
}

- mycar.component.html

<div class="mycar">
  My Car Component (Child)

  <h2>Car Name: <b>Show Car name from Parent</b></h2>

</div>

Now lets embed mycar component inside app component so that we can have parent-child relationship between them.

<div class="myapp">
  App Component (Parent)
  <my-car></my-car>
</div>

So we have added <my-car> inside app component. Now let see the output on browser.




Yellow section contains the app component and blue section contains the My Car component.

Now we know we have "latestCar" variable which contains the information of the car and we want to display it inside mycar component (child) component but "latestCar" variable is inside out app component so how can we pass it to our child i.e. mycar component.

Here we can use @Input decorator. 

Well we know our app component contains the <my-car> selector inside it template. So we can pass data (latestCar) like this:


<div class="myapp">
  App Component (Parent)
  <my-car [car]="latestCar"></my-car>
</div>

so here we have passed "latestCar" to my-car component and [car] will hold the value of it so [car] would be act like an input for my-car component.

Now to receive the [car] input inside my-car compoent we need to use @Input decorator.

Let see our updated my-car component.ts

- my-car.component.ts

import { Component, Input, Output } from '@angular/core';

@Component({
  selector: 'my-car',
  templateUrl: './mycar.component.html',
  styles:[`.mycar{background:blue; color:white; padding:20px; margin-top:40px;}`]
})
export class MyCarComponent  {
  @Input() car: string;
}

To use @Input decorator we must have to u

- mycar.component.html

<div class="mycar">
  My Car Component (Child)
<h2>Car Name: <b>{{car}}</b></h2>
</div>

Output:


@Output:

Now if we want to pass data from our mycar component to app component we need to use @Output decorator. So here our parent i.e. app component will receive the data from child.

Let start with @Output now.

So we got a car from our app component and we would love to share our feedback of a car to app component i.e. parent.

Let add some radio buttons along with button to share our feedback.

- mycar.component.ts


export class MyCarComponent {

  rating: string = "";

  // receiving an input from parent (app.component)
  @Input() car: string;  

  rateMyCar(){
    console.log(this.rating);
  }
}

- mycar.component.html


<div class="mycar">My Car Component (Child) <h2>Car Name: <b>{{car}}</b></h2>

  <label>Rate: </label>
  <input type="radio" [(ngModel)]="rating"  value="Excellent"> Excellent
  <input type="radio" [(ngModel)]="rating" value="Best"> Best
  <input type="radio" [(ngModel)]="rating" value="Good"> Good
  <input type="radio" [(ngModel)]="rating" value="Average"> Average
  <input type="radio" [(ngModel)]="rating" value="Bad"> Bad

  <button type="button" (click)="rateMyCar()">Submit</button>

</div>

So here in our mycar component we have created one variable "rating" and using two way data biding (ngModel) we'll receive the selected value of it
on click of submit button which will call the rateMyCar() event.

So far so good now, calling rateMyCar() we are getting selected radio button value but we are not doing anything with it just by displayin it on console.
Our goal is to send rating to our parent component so that app component could show rating inside it yellow section. Let see how we could achive this.

- First update mycar.component.ts

- Import EventEmitter and @Output decorator


import { Component, Input, Output, EventEmitter } from '@angular/core';

Now use @Output decorator


@Output() carRatingFromUser: EventEmitter<string> = new EventEmitter();


@Output binds a property of the type of angular EventEmitter class. This property name becomes custom event name for the calling component. We'll use the emit method of EventEmitter class to send rating (this.rating) to app component.
Our final mycar.component.ts would look like this:

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'my-car',
  templateUrl: './mycar.component.html',
  styles:[`.mycar{background:blue; color:white; padding:20px; margin-top:40px;}`]
})

export class MyCarComponent {
  rating: string = "";

  // receiving an input from parent (app.component)
  @Input() car: string;  

  // sending carRatingFromUser to parent component (app.component)
  @Output() carRatingFromUser: EventEmitter<string> = new EventEmitter();

  rateMyCar(){
    console.log(this.rating);
    this.carRatingFromUser.emit(this.rating);
  }
}

In app.component.html we will bind the custom event.


<div class="myapp">
  App Component (Parent)
  <my-car [car]="latestCar" (carRatingFromUser)="showCarRating($event)"></my-car>
</div>

- app.component.ts: Declare showCarRating() function to fetch carRatingFromUser.


import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styles:[`.myapp{background:yellow; padding:50px;}`]
})
export class AppComponent  {
  latestCar:string = "BMW";
  userRating:string;
  
  // delcare showCarRating function
  showCarRating(rating){
 this.userRating = rating;
  }
}

Showing user rating in app.component template


<div class="myapp">
  App Component (Parent)
  <my-car [car]="latestCar" (carRatingFromUser)="showCarRating($event)"></my-car>
  <span *ngIf="userRating">{{userRating}}</span> 
</div>

Go to browser, select radio button and click on submit button. You would be able to see result on app component's yellow section.

Hope this help.

No comments:

Post a Comment