Simple selection by Angular CDK
Tags: angular, html, tutorial
Published: 2021-09-03 13:34:21

### 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:
```JSON
            "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:
```typescript
  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:
```typescript
selectionModel = new SelectionModel(true);
```
The import for that model is:
```typescript
import { SelectionModel } from '@angular/cdk/collections';
```
Inside `ngOnInit` life cycle I subscribed to changes on selection (just for proof that selection is working):
```typescript
  ngOnInit(): void {
    this.selectionModel.changed
      .pipe(pluck('source', 'selected'))
      .subscribe((selected) => console.log(selected));
  }
```
`selectionModel` has property `changed` which emits selected data. `pluck` [operator](https://rxjs.dev/api/operators/pluck) 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.
```typescript
  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`.
```typescript
  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:
```HTML
<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](https://github.com/TomWebwalker/simple-selection) to repo.