Angular Observable Inputs
Sometimes using an Observable for an Angular @Input
is a good idea as it simplifies handling changes in your components. In this post we'll look at using Observable @Input
to do just that.
We'll use two components AdminComponent (the parent) and EmployeeComponent (the child). The AdminComponent will pass employees to the EmployeeComponent via the EmployeeComponents @Input
which will be an Observable of Employees, Observable<Employee[]>
@Input as Observable of Employees
Lets start by defining a simple Employee interface so we can properly type our @Input
.
// employee.ts
export interface Employee {
name: string;
age: number;
}
Then we need to create our parent component, the AdminComponent.
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Employee } from './employee';
@Component({
selector: 'app-admin',
template: `<app-employee [employees]="employees$"></app-employee>`,
styleUrls: ['./admin.component.scss'],
})
export class AdminComponent {
employees = [
{ name: 'bob', age: 45 },
{ name: 'angie', age: 33 },
];
employees$: Observable<any> = of(this.employees);
}
Next we create our child component, the EmployeeComponent.
import { Component, Input } from '@angular/core';
import { Observable } from 'rxjs';
import { Employee } from './employee';
@Component({
selector: 'app-employee',
template: `<div *ngFor="let employee of employees$ | async">
<p>name {{ employee.name }}</p>
<p>age {{ employee.age }}</p>
</div>`,
styleUrls: ['./employee.component.scss']
})
export class EmployeeComponent {
@Input('employees') employees$!: Observable<Employee[]>;
}
Now we have all we need to display a list of employees, note we don't need any lifecycle hooks, such as OnInit or OnChanges, making our code slightly less verbose.
We now have a base setup for our Observable Input that we can add additional operators too if we need them. Lets add a map operator that will only return employees over the age of 40. We take the array of employees and filter them as required.
employees$: Observable<any> = of(this.employees).pipe(
map((employees: Employee[]) => employees.filter(employee => employee.age > 40))
);
Two Employee Sources
Sometimes you'll have multiple sources that need to be combined and used as an input. Below shows two employee sources that are joined and mapped to produce a single array of employees to the EmployeeComponent.
employeesA: Employee[] = [
{ name: 'bob', age: 45 },
{ name: 'angie', age: 33 },
];
employeesB: Employee[] = [
{ name: 'fred', age: 45 },
{ name: 'sue', age: 33 },
];
employeesA$: Observable<any> = of(this.employeesA);
employeesB$: Observable<any> = of(this.employeesB);
employees$ = forkJoin([this.employeesA$, this.employeesB$])
.pipe(
map(([employeesA, employeesB]) => [...employeesA, ...employeesB])
);