Description
In my recent projects, I had to implement a table with select all or single row functionality. Angular CDK library has SelectionModel, which makes that selection easy to implement. 
Setup project
My project is using Angular, so I've created a new project (using Angular CLI) by typing in console:
ng new simple-selection
My demo project is quite simple, so I answered "No" on routing and selected SCSS as my stylesheet format. To install Angular CDK, I typed in the project root:
npm i @angular/cli
Additionally (not required), I used Bootstrap to have provided styling; I added it by typing:
npm i bootstrap
In angular.json file, I added boostrap.scss import to projects.architect.build.options.styles so it looks now:
            "styles": [
              "src/styles.scss",
              "node_modules/bootstrap/scss/bootstrap.scss"
            ],
Now my project is ready to develop a table with selection.
Selection
Typescript part
In app.component.scss I created some dummy variable with list of rows:
  rows: any[] = [
    { id: 1, name: 'test1', email: 'test1@test.com' },
    { id: 2, name: 'test2', email: 'test2@test.com' },
    { id: 3, name: 'test3', email: 'test3@test.com' },
    { id: 4, name: 'test4', email: 'test4@test.com' },
    { id: 5, name: 'test5', email: 'test5@test.com' },
  ];
Next, I added variable with selection model from CDK:
selectionModel = new SelectionModel(true);
The import for that model is:
import { SelectionModel } from '@angular/cdk/collections';
Inside ngOnInit life cycle I subscribed to changes on selection (just for proof that selection is working):
  ngOnInit(): void {
    this.selectionModel.changed
      .pipe(pluck('source', 'selected'))
      .subscribe((selected) => console.log(selected));
  }
selectionModel has property changed which emits selected data. pluck operator takes arguments to get (nested) property from emitted object. 
To know if all rows are selected, I've made getter which compares length of rows with selected items lengt. If it returns with true then all rows are selected.
  get areAllSelected(): boolean {
    return this.rows.length === this.selectionModel.selected.length;
  }
To select all rows, I am mapping them and adding each row to selection. To deselect all, I am using clear method from selectionModel.
  onSelectAllChange(): void {
    if (this.areAllSelected) {
      this.selectionModel.clear();
    } else {
      this.rows.map((row) => this.selectionModel.select(row));
    }
  }
HTML part
HTML contains basic table from bootstrap documentation. There are two key elements:
header checkbox uses the areAllSelected property check state and the onSelectAllChange method to select or deselect all rows on the change event.
Second element is checkbox from row column. On change event it uses toggle method from selectionModel and pass current row. To know if checkbox should be checked it uses isSelected method from selectionModel and also passes current row. Full HTML code:
<table class="table">
  <thead>
    <tr>
      <th>
        <input
          class="form-check-input"
          type="checkbox"
          id="table-select-all"
          aria-label="Rows selection"
          (change)="onSelectAllChange()"
          [checked]="areAllSelected">
      </th>
      <th scope="col">ID</th>
      <th scope="col">Name</th>
      <th scope="col">Email</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let row of rows; index as i">
      <td>
        <input
          class="form-check-input"
          type="checkbox"
          [id]="'row-checkbox-' + i"
          aria-label="Row selection"
          (change)="selectionModel.toggle(row)"
          [checked]="selectionModel.isSelected(row)">
      </td>
      <td>{{ row.id }}</td>
      <td>{{ row.name }}</td>
      <td>{{ row.email }}</td>
    </tr>
  </tbody>
</table>
Link to repo.
 

 
    
Top comments (0)