How to create a generic reusable component in angular

Viewed 83

I have an Angular 13 app & building a common reusable component named app-table

html

    <table class="table table-striped">
    <thead>
        <tr>
            <th>Name</th>
            <th>Email</th>
            <th>DOB</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let item of tableItems">
            <td> {{ item.name }}</td>
            <td> {{ item.email } </td>
            <td> {{ item.dob }} </td>
        </tr>
    </tbody>
</table>

component.ts

    import { Component, Input, OnInit} from '@angular/core';
    import { TableItem } from './model/tableItem';

    @Component({
     selector: 'app-table',
     templateUrl: './table.component.html',
     styleUrls: ['./table.component.css'],
    })

    export class TableComponent implements OnInit {
     @Input() tableItems: TableItem[] = [];
   
     constructor() {}

     ngOnInit(): void {}
     }

Currently, this is reusable across 4-5 places, however reusability of this component is tied to name, email, dob fields. I want to make it generic so that any model could be linked to this component & can be reused across the app.

Please suggest. Thanks!

2 Answers

There are a lot of ways to do it, the simplest one would be add another input which accepts a list of columns, then you can populate your table header based on that list. something like this.

    <table class="table table-striped">
      <thead>
          <tr>
            <ng-container *ngFor="let column of columns">
              <th>{{column}}</th>
            </ng-container>
          </tr>
      </thead>
      <tbody>
          <tr *ngFor="let item of tableItems">
            <ng-container *ngFor="let column of columns">
              <td>{{item[column]}}</td>
            </ng-container>
          </tr>
      </tbody>
    </table>  

and your component will look like this.

    columns = ['name','email','dob']
    tableItems = [
      {
        name: 'test',
        dob: '31/12/1990',
        email: 'email@test.com'
      }
    ];

but table components are really difficult to maintain and extend, they require a lot of effort, it's not something you casually do. I would suggest finding a library that built a really good table component and using it, depending on your requirements, few examples would be ngx easy table, angular cdk table, and so on.

You can get the column names from object keys and iterate over them using *ngFor, for example:

In your TableComponent template file

<table>
  <thead>
    <tr>
      <th *ngFor="let header of data[0] | keyvalue: sortNull">{{ header.key }}</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let row of data">
      <td *ngFor="let cell of row | keyvalue: sortNull">{{ cell.value }}</td>
    </tr>
  </tbody>
</table>

And in typescript file

export class TableComponent {
  @Input() data: any[] = [];

  sortNull() { return 0; }
}

sortNull() is just a compartor function that always return 0 to maintain the order of insertion for your key/value pairs

And for usage

@Component({
  selector: 'app-user-list',
  template: `<app-table [data]="data"></app-table>`
})
export class UserListComponent {

  data = [ 
    { name: 'John', email: 'john@example.com', dob: '2001/01/01' }, 
    { name: 'Jane', email: 'jane@example.com', dob: '2002/02/02' }, 
    { name: 'Kane', email: 'kane@example.com', dob: '2003/03/03' }, 
  ]
}
Related