An Overview of Angular Component Interaction

John Au-Yeung - Jan 23 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at some examples of Angular component interaction.

Pass Data From Parent to Child with Input Binding

We can pass data from a parent component to a child component by using the @Input operator in the child component.

For example, we can write the following to do that:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';

@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

app.component.ts :

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

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  person = { name: 'Jane' }  
}
Enter fullscreen mode Exit fullscreen mode

app.component.html :

<app-person [person]='person'></app-person>
Enter fullscreen mode Exit fullscreen mode

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Input() person;  
}
Enter fullscreen mode Exit fullscreen mode

person.component.html :

<p>{{person.name}}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the AppComponent , which is the parent of PersonComponent .

In AppComponent , we have the person field, which is passed into the person property of app-person .

Then in the PersonComponent , we have:

@Input() person;
Enter fullscreen mode Exit fullscreen mode

to get the person input value that’s passed in. Then we display the name property with:

<p>{{person.name}}</p>
Enter fullscreen mode Exit fullscreen mode

in the template.

Then we should see ‘Jane’ on the screen.

Intercept Input Property Changes With a Setter

We can add a setter to the @Input decorator to intercept the value that’s set and change it to something else.

For example, we can write the following code:

app.component.ts :

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

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  person = { name: 'Jane' }  
}
Enter fullscreen mode Exit fullscreen mode

app.component.html :

<app-person [person]='person'></app-person>  
<app-person [person]='undefined'></app-person>
Enter fullscreen mode Exit fullscreen mode

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  private _person = {};  
  @Input()  
  set person(person) {  
    this._person = person || { name: 'Foo' };  
  } 

  get person() { return this._person; }  
}
Enter fullscreen mode Exit fullscreen mode

person.component.html :

<p>{{person.name}}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we added getters and setters for the person property in the PersonComponent .

In the setter, we set person depending if person is defined or not. If it’s not then we set this._person to a default object. Otherwise, we set this._person to whatever the value is passed in from the parent.

In the getter, we get the person value that was set by return this._person .

The first line should display ‘Jane’ since we passed in person into app-person .

The second line should display ‘Foo’ since we pass undefined as the value of person .

Intercept Input Property Changes with ngOnChanges()

We can use ngOnChanges to intercept the change and then set the value passed into the @Input field by changing it to what we want.

For example, we can write the following code:

app.component.ts :

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

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  person = { name: 'Jane' }  
}
Enter fullscreen mode Exit fullscreen mode

app.component.html :

<app-person [person]='person'></app-person>  
<app-person [person]='undefined'></app-person>
Enter fullscreen mode Exit fullscreen mode

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Input() person; 

  ngOnChanges(change: { [propKey: string]: SimpleChange }) {  
    this.person = change.person.currentValue || { name: 'Foo' };  
  }  
}
Enter fullscreen mode Exit fullscreen mode

person.component.html :

<p>{{person.name}}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the ngOnChanges hook in the PersonComponent .

It takes a change parameter that has the value of the change. In the hook, we check if change.person.currentValue is defined.

If it’s not, then we set this.person to { name: ‘Foo’ } . Otherwise, we set this.person to change.person.currentValue .

Then we should see the same thing as in the first example.

Parent Listens for Child Event

We can call emit on an EventEmitter in the child component and then listen to the emitted event in the parent.

For instance, we can do that as follows:

app.component.ts :

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

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  person = {};  
}
Enter fullscreen mode Exit fullscreen mode

app.component.html :

<app-person (emittedPerson)='person = $event'></app-person>  
<p>{{person.name}}</p>
Enter fullscreen mode Exit fullscreen mode

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Output() emittedPerson = new EventEmitter(); emitPerson() {  
    this.emittedPerson.emit({ name: 'Jane' });  
  }  
}
Enter fullscreen mode Exit fullscreen mode

person.component.html :

<button (click)='emitPerson()'>Get Person</button>
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the emitPerson method in the PersonComponent , which calls emit on the emittedPerson emitter, where we pass in the payload to send to the parent.

Then in app.component.html , we have:

<app-person (emittedPerson)='person = $event'></app-person>
Enter fullscreen mode Exit fullscreen mode

to listen to the emittedPerson event from app-person and then set the person field to the $event variable passed in from the child by calling emit .

Then we have:

<p>{{person.name}}</p>
Enter fullscreen mode Exit fullscreen mode

to display the name property value from the emitted object.

Therefore, when we click the Get Person button, we should see ‘Jane’ displayed.

Conclusion

We can pass data from a parent to a child by using the @Input decorator in the child.

To pass data from a child to a parent, we can emit an event from the child. Then the parent can listen to it and then run code to do something with it.

$event has the object that’s passed in with the emit call.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .