Top

Tables


# First Name Last Name Username Role Country
1 Alexander Orton @mdorton Admin USA
2 John Deo Deo @johndeo User USA
3 Randy Orton the Bird @twitter admin UK
4 Randy Mark Ottandy @mdothe user AUS
5 Ram Jacob Thornton @twitter admin IND
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Username</th>
<th scope="col">Role</th>
<th scope="col">Country</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Alexander</td>
<td>Orton</td>
<td>@mdorton</td>
<td>Admin</td>
<td>USA</td>
</tr>
<tr>
<th scope="row">2</th>
<td>John Deo</td>
<td>Deo</td>
<td>@johndeo</td>
<td>User</td>
<td>USA</td>
</tr>
<tr>
<th scope="row">3</th>
<td>Randy Orton</td>
<td>the Bird</td>
<td>@twitter</td>
<td>admin</td>
<td>UK</td>
</tr>
<tr>
<th scope="row">4</th>
<td>Randy Mark</td>
<td>Ottandy</td>
<td>@mdothe</td>
<td>user</td>
<td>AUS</td>
</tr>
<tr>
<th scope="row">5</th>
<td>Ram Jacob</td>
<td>Thornton</td>
<td>@twitter</td>
<td>admin</td>
<td>IND</td>
</tr>
</tbody>
</table>
</div>

You have to change only table sizing class.you can use table-*(sm,lg,xl,xs) class

# First Last Handle
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
<div class="table-responsive">
 <table class="table table-sm">
  <thead>
   <tr>
    <th scope="col">#</th>
    <th scope="col">First</th>
    <th scope="col">Last</th>
    <th scope="col">Handle</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <th scope="row">1</th>
    <td>Mark</td>
    <td>Otto</td>
    <td>@mdo</td>
   </tr>
   <tr>
    <th scope="row">2</th>
    <td>Jacob</td>
    <td>Thornton</td>
    <td>@fat</td>
   </tr>
   <tr>
    <th scope="row">3</th>
    <td colspan="2">Larry the Bird</td>
    <td>@twitter</td>
   </tr>
  </tbody>
 </table>
</div>
Name Position Office Age Start date Salary
Tiger Nixon System Architect Edinburgh 61 2011/04/25 $320,800
Garrett Winters Accountant Tokyo 63 2011/07/25 $170,750
<table class="display" id="basic-2">
<form>
	<div class="mb-3 row">
		<label for="table-complete-search" class="col-xs-3 col-sm-auto col-form-label">Full text search:</label>
		<div class="col-xs-3 col-sm-auto">
			<input
				id="table-complete-search"
				type="text"
				class="form-control"
				name="searchTerm"
				[(ngModel)]="service.searchTerm"
			/>
		</div>
		@if (service.loading$ | async) {
			<span class="col col-form-label">Loading...</span>
		}
	</div>

	<table class="table table-striped">
		<thead>
			<tr>
				<th scope="col">#</th>
				<th scope="col" sortable="name" (sort)="onSort($event)">Country</th>
				<th scope="col" sortable="area" (sort)="onSort($event)">Area</th>
				<th scope="col" sortable="population" (sort)="onSort($event)">Population</th>
			</tr>
		</thead>
		<tbody>
			@for (country of countries$ | async; track country.id) {
				<tr>
					<th scope="row">{{ country.id }}</th>
					<td>
						<img
							[src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag"
							[alt]="'The flag of ' + country.name"
							class="me-2"
							style="width: 20px"
						/>
						<ngb-highlight [result]="country.name" [term]="service.searchTerm" />
					</td>
					<td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm" /></td>
					<td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm" /></td>
				</tr>
			} @empty {
				<tr>
					<td colspan="4" style="text-align: center">No countries found</td>
				</tr>
			}
		</tbody>
	</table>

	<div class="d-flex justify-content-between p-2">
		<ngb-pagination [collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
		</ngb-pagination>

		<select class="form-select" style="width: auto" name="pageSize" [(ngModel)]="service.pageSize">
			<option [ngValue]="2">2 items per page</option>
			<option [ngValue]="4">4 items per page</option>
			<option [ngValue]="6">6 items per page</option>
		</select>
	</div>
</form>
ts files

import { AsyncPipe, DecimalPipe } from '@angular/common';
import { Component, QueryList, ViewChildren } from '@angular/core';
import { Observable } from 'rxjs';

import { Country } from './country';
import { CountryService } from './country.service';
import { NgbdSortableHeader, SortEvent } from './sortable.directive';
import { FormsModule } from '@angular/forms';
import { NgbHighlight, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';

@Component({
	selector: 'ngbd-table-complete',
	imports: [DecimalPipe, FormsModule, AsyncPipe, NgbHighlight, NgbdSortableHeaderDirective, NgbPaginationModule],
	templateUrl: './table-complete.html',
	providers: [CountryService, DecimalPipe],
})
export class NgbdTableComplete {
	countries$: Observable;
	total$: Observable;


   readonly headers = viewChildren(NgbdSortableHeaderDirective);
    public service = inject(CountryService);
  
    constructor() {
      this.countries$ = service.countries$;
      this.total$ = service.total$;
    }


    onSort({ column, direction }: SortEvent) {
      this.headers().forEach(header => {
        if (header.sortable() !== column) {
          header.direction = '';
        }
      });
      this.direction = direction;
      this.service.sortColumn = column;
      this.service.sortDirection = direction;
    }
}

service file

import { Injectable, PipeTransform } from '@angular/core';

import { BehaviorSubject, Observable, of, Subject } from 'rxjs';

import { Country } from './country';
import { COUNTRIES } from './countries';
import { DecimalPipe } from '@angular/common';
import { debounceTime, delay, switchMap, tap } from 'rxjs/operators';
import { SortColumn, SortDirection } from './sortable.directive';

interface SearchResult {
	countries: Country[];
	total: number;
}

interface State {
	page: number;
	pageSize: number;
	searchTerm: string;
	sortColumn: SortColumn;
	sortDirection: SortDirection;
}

const compare = (v1: string | number, v2: string | number) => (v1 < v2 ? -1 : v1 > v2 ? 1 : 0);

function sort(countries: Country[], column: SortColumn, direction: string): Country[] {
	if (direction === '' || column === '') {
		return countries;
	} else {
		return [...countries].sort((a, b) => {
			const res = compare(a[column], b[column]);
			return direction === 'asc' ? res : -res;
		});
	}
}

function matches(country: Country, term: string, pipe: PipeTransform) {
	return (
		country.name.toLowerCase().includes(term.toLowerCase()) ||
		pipe.transform(country.area).includes(term) ||
		pipe.transform(country.population).includes(term)
	);
}

@Injectable({ providedIn: 'root' })
export class CountryService {
  private pipe = inject(DecimalPipe);
	private _loading$ = new BehaviorSubject(true);
	private _search$ = new Subject();
	private _countries$ = new BehaviorSubject([]);
	private _total$ = new BehaviorSubject(0);

	private _state: State = {
		page: 1,
		pageSize: 4,
		searchTerm: '',
		sortColumn: '',
		sortDirection: '',
	};

	constructor() {
		this._search$
			.pipe(
				tap(() => this._loading$.next(true)),
				debounceTime(200),
				switchMap(() => this._search()),
				delay(200),
				tap(() => this._loading$.next(false)),
			)
			.subscribe((result) => {
				this._countries$.next(result.countries);
				this._total$.next(result.total);
			});

		this._search$.next();
	}

	get countries$() {
		return this._countries$.asObservable();
	}
	get total$() {
		return this._total$.asObservable();
	}
	get loading$() {
		return this._loading$.asObservable();
	}
	get page() {
		return this._state.page;
	}
	get pageSize() {
		return this._state.pageSize;
	}
	get searchTerm() {
		return this._state.searchTerm;
	}

	set page(page: number) {
		this._set({ page });
	}
	set pageSize(pageSize: number) {
		this._set({ pageSize });
	}
	set searchTerm(searchTerm: string) {
		this._set({ searchTerm });
	}
	set sortColumn(sortColumn: SortColumn) {
		this._set({ sortColumn });
	}
	set sortDirection(sortDirection: SortDirection) {
		this._set({ sortDirection });
	}

	private _set(patch: Partial) {
		Object.assign(this._state, patch);
		this._search$.next();
	}

	private _search(): Observable {
		const { sortColumn, sortDirection, pageSize, page, searchTerm } = this._state;

		// 1. sort
		let countries = sort(COUNTRIES, sortColumn, sortDirection);

		// 2. filter
		countries = countries.filter((country) => matches(country, searchTerm, this.pipe));
		const total = countries.length;

		// 3. paginate
		countries = countries.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);
		return of({ countries, total });
	}
}
firective file

import { Directive, Input, input, output } from '@angular/core';

import { supportDB } from '../data/support-ticket/support-ticket';

export type SortColumn = keyof supportDB | '';
export type SortDirection = 'asc' | 'desc' | '';
const rotate: { [key: string]: SortDirection } = { asc: 'desc', desc: '', '': 'asc' };

export interface SortEvent {
  column: SortColumn;
  direction: SortDirection;
}

@Directive({
  selector: 'th[sortable]',
  host: {
    '[class.asc]': 'direction === "asc"',
    '[class.desc]': 'direction === "desc"',
    '(click)': 'rotate()',
  },
})
export class NgbdSortableHeader2Directive {
  constructor() {}
  readonly sortable = input('');
  @Input() direction: SortDirection = '';
  readonly sort = output();

  rotate() {
    this.direction = rotate[this.direction];
    this.sort.emit({ column: this.sortable(), direction: this.direction });
  }
}

data file

export const COUNTRIES: Country[] = [
	{
		id: 1,
		name: 'Russia',
		flag: 'f/f3/Flag_of_Russia.svg',
		area: 17075200,
		population: 146989754,
	},
	{
		id: 2,
		name: 'France',
		flag: 'c/c3/Flag_of_France.svg',
		area: 640679,
		population: 64979548,
	},
	{
		id: 3,
		name: 'Germany',
		flag: 'b/ba/Flag_of_Germany.svg',
		area: 357114,
		population: 82114224,
	},
	{
		id: 4,
		name: 'Portugal',
		flag: '5/5c/Flag_of_Portugal.svg',
		area: 92090,
		population: 10329506,
	},
	{
		id: 5,
		name: 'Canada',
		flag: 'c/cf/Flag_of_Canada.svg',
		area: 9976140,
		population: 36624199,
	},
	{
		id: 6,
		name: 'Vietnam',
		flag: '2/21/Flag_of_Vietnam.svg',
		area: 331212,
		population: 95540800,
	},
	{
		id: 7,
		name: 'Brazil',
		flag: '0/05/Flag_of_Brazil.svg',
		area: 8515767,
		population: 209288278,
	},
	{
		id: 8,
		name: 'Mexico',
		flag: 'f/fc/Flag_of_Mexico.svg',
		area: 1964375,
		population: 129163276,
	},
	{
		id: 9,
		name: 'United States',
		flag: 'a/a4/Flag_of_the_United_States.svg',
		area: 9629091,
		population: 324459463,
	},
	{
		id: 10,
		name: 'India',
		flag: '4/41/Flag_of_India.svg',
		area: 3287263,
		population: 1324171354,
	},
	{
		id: 11,
		name: 'Indonesia',
		flag: '9/9f/Flag_of_Indonesia.svg',
		area: 1910931,
		population: 263991379,
	},
	{
		id: 12,
		name: 'Tuvalu',
		flag: '3/38/Flag_of_Tuvalu.svg',
		area: 26,
		population: 11097,
	},
	{
		id: 13,
		name: 'China',
		flag: 'f/fa/Flag_of_the_People%27s_Republic_of_China.svg',
		area: 9596960,
		population: 1409517397,
	},
];
interface file

export interface Country {
	id: number;
	name: string;
	flag: string;
	area: number;
	population: number;
}