Wednesday, February 12, 2020

Angular Table Filter Component

Creating The Filter
The first thing we need is a filtered list of games to bind to. We’ll create a property for this like so.
game-list.component.ts
import {Component, OnInit} from '@angular/core';
import {IGame} from './game';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html',
    styleUrls: ['./game-list.component.css']
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    listItem = 'Mario';
    filteredGames: IGame[] = [];
    games: IGame[] = [...];

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}
We can add that property like so.
game-list.component.ts
import {Component, OnInit} from '@angular/core';
import {IGame} from './game';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html',
    styleUrls: ['./game-list.component.css']
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    _listFilter = '';
    filteredGames: IGame[] = [];
    games: IGame[] = [...];

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}
This syntax looks like so.
Angular Table Filter Component

game-list.component.ts
import {Component, OnInit} from '@angular/core';
import {IGame} from './game';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html',
    styleUrls: ['./game-list.component.css']
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    _listFilter = '';
    get listFilter(): string {
        return this._listFilter;
    }
    set listFilter(value: string) {
        this._listFilter = value;
    }
    filteredGames: IGame[] = [];
    games: IGame[] = [...];

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}
The filtered games array should be set to the filtered list of games like so.
game-list.component.ts
import {Component, OnInit} from '@angular/core';
import {IGame} from './game';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html',
    styleUrls: ['./game-list.component.css']
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    _listFilter = '';
    get listFilter(): string {
        return this._listFilter;
    }

    set listFilter(value: string) {
        this._listFilter = value;
        this.filteredGames = this.listFilter ? this.doFilter(this.listFilter) : this.games;
    }

    filteredGames: IGame[] = [];
    games: IGame[] = [...];

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}

Adding The Filter Logic

The doFilter() method is not yet defined, so let’s create that now.
game-list.component.ts
import { Component, OnInit } from '@angular/core';
import { IGame } from './game';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html',
    styleUrls: ['./game-list.component.css']
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    _listFilter = '';
    get listFilter(): string {
        return this._listFilter;
    }

    set listFilter(value: string) {
        this._listFilter = value;
        this.filteredGames = this.listFilter ? this.doFilter(this.listFilter) : this.games;
    }

    filteredGames: IGame[] = [];
    games: IGame[] = [...];

    doFilter(filterBy: string): IGame[] {
        filterBy = filterBy.toLocaleLowerCase();
        return this.games.filter((game: IGame) =>
            game.gameName.toLocaleLowerCase().indexOf(filterBy) !== -1);
    }

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}

Configuring The Constructor

In order for this to work, we’ll need to set up some default data in the constructor. The constructor automatically runs when the component is initialized. This follows the same idea as a constructor in any object-oriented language. In the constructor, the list of games should be assigned to the filtered games to begin. The default value of the filter string will simply be an empty string as we don’t want any filtering to happen until the user types something into the filter text box.
game-list.component.ts
import { Component, OnInit } from '@angular/core';
import { IGame } from './game';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html',
    styleUrls: ['./game-list.component.css']
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    _listFilter = '';
    get listFilter(): string {
        return this._listFilter;
    }

    set listFilter(value: string) {
        this._listFilter = value;
        this.filteredGames = this.listFilter ? this.doFilter(this.listFilter) : this.games;
    }

    filteredGames: IGame[] = [];
    games: IGame[] = [...];

    constructor() {
        this.filteredGames = this.games;
        this.listFilter = '';
    }

    doFilter(filterBy: string): IGame[] {
        filterBy = filterBy.toLocaleLowerCase();
        return this.games.filter((game: IGame) =>
            game.gameName.toLocaleLowerCase().indexOf(filterBy) !== -1);
    }

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}

Updating The Component Template

In order for this to work, we also need to tweak the component template just a bit. See the highlighted lines.
game-list.component.html
<div class='card'>
    <div class='card-header'>
        {{pageTitle}}
    </div>
    <div class='card-body'>
        <div class="row">
            <div class="col-3">
                <input [(ngModel)]="listFilter" type="text" class="form-control" id="filterInput" placeholder="Type to filter">
            </div>
            <div class="col">
                <div *ngIf='listFilter' class="form-text text-muted">Filtered by: {{listFilter}}</div>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table' *ngIf='games && games.length'>
                <thead>
                <tr>
                    <th>
                        <button class='btn btn-primary'
                                (click)='toggleImage()'>
                            {{showImage ? 'Hide&nbsp;' : 'Show'}} Image
                        </button>
                    </th>
                    <th style="color:firebrick">Game</th>
                    <th>Part#</th>
                    <th>Release Date</th>
                    <th>Cost</th>
                    <th>5 Thumb Rating</th>
                </tr>
                </thead>
                <tbody>
                <tr *ngFor='let game of filteredGames'>
                    <td>
                        <img *ngIf='showImage'
                             [src]='game.imageUrl'
                             [title]='game.gameName'
                             [style.width.px]='imageWidth'
                             [style.margin.px]='imageMargin'>
                    </td>
                    <td>{{ game.gameName }}</td>
                    <td>{{ game.gameCode | lowercase }}</td>
                    <td>{{ game.releaseDate }}</td>
                    <td>{{ game.price | currency:'USD':'symbol':'1.2-2' }}</td>
                    <td>{{ game.thumbRating }}</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div class="card-footer text-muted text-right">
        <small>Powered by Angular</small>
    </div>
</div>
Angular Table Filter Component