Angular Component Inheritance and Template Swapping
Angular components are a fantastic way to break up our web apps into small easy to understand pieces of UI code. Sometimes in specific apps, we have drastic layout differences for the same data being displayed. An example could be differences between mobile and desktop layouts. Sometimes it could be showing our data in different formats such as a list view vs. a table view.
In these instances many times our component is a presentation component displaying this data and we have the same Inputs
and Output
data. In these cases, it would be beneficial to be able to swap the templates when displaying the data in different formats. In this post, we are going to use Angular Component Inheritance to derive two subcomponents to display some data. One component will be a list view and another a table view.
Base Component Class
Overall I do not recommend using Component Inheritance but rather use composition to mix and match multiple components together. In this particular use case Component Inheritance is rather helpful when our templates have drastic differences in markup but display the same data.
First, we need to define our base class that contains all of the logic we want to share.
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Employee } from './interfaces';
@Component({
selector: 'app-employee',
template: ''
})
export class EmployeeComponent {
@Input() employees: Employee[] = [];
@Output() select = new EventEmitter();
heading = 'Employees';
selectEmployee(employee: Employee) {
this.select.emit(employee);
}
}
Our base employee component has one input and one output. It takes in a list of employee objects to display and emits an event to the parent component when an employee is selected. Notice carefully that we don't have a template. This is because we are going to extend this component into a employee-list.component
and employee-table.component
.
Component Inheritance
import { Component } from '@angular/core';
import { EmployeeComponent } from './employee.component';
@Component({
selector: 'app-employee-list',
template: `
<h1>{{heading}}</h1>
<ul>
<li *ngFor="let employee of employees">
{{employee.firstName}} {{employee.lastName}} <br>
{{employee.email}} <br>
<button (click)="selectEmployee(employee)">Select</button>
</li>
</ul>
`
})
export class EmployeeListComponent extends EmployeeComponent {
heading = 'Employee List';
}
Above is our component that inherits our base component class. We do this by extending the class with this line of code EmployeeListComponent extends EmployeeComponent
. We inherit all the input and outputs of the base class. In our list component, we override the decorator with a list template and override the heading property. Now we can reuse all of the base class logic with a different template. Below is our other component the employee-table.component
.
import { Component } from '@angular/core';
import { EmployeeComponent } from './employee.component';
@Component({
selector: 'app-employee-table',
template: `
<h1>{{heading}}</h1>
<table>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Email</td>
</tr>
<tr *ngFor="let employee of employees">
<td>{{employee.firstName}}</td>
<td>{{employee.lastName}}</td>
<td>{{employee.email}}</td>
<td><button (click)="selectEmployee(employee)">Select</button></td>
</tr>
</table>
`
})
export class EmployeeTableComponent extends EmployeeComponent { }
We can see above that we can share our base component logic and have a drastically different template that shares the same behavior.
<app-employee-list
[employees]="employees"
(select)="selectEmployee($event)"
></app-employee-list>
<app-employee-table
[employees]="employees"
(select)="selectEmployee($event)"
></app-employee-table>
In the code above we can see both inherited child components now share the same public API.
While composition is the preferred way to use components using component inheritance can provide benefits in specific use cases. This is especially useful for UI kits and component libraries. Check out the running demo below!