Data Table
Features Demonstrated
Code
View Model (data-table.ts)
interface User {
id: number;
name: string;
email: string;
role: string;
status: 'active' | 'inactive' | 'pending';
lastLogin: Date;
tasksCompleted: number;
}
type SortColumn = 'name' | 'email' | 'role' | 'status' | 'lastLogin' | 'tasksCompleted';
type SortDirection = 'asc' | 'desc';
export class DataTable {
// Raw data (would normally come from API)
private allUsers: User[] = [
{
id: 1,
name: 'Alice Johnson',
email: '[email protected]',
role: 'Admin',
status: 'active',
lastLogin: new Date('2025-01-08'),
tasksCompleted: 127
},
{
id: 2,
name: 'Bob Smith',
email: '[email protected]',
role: 'User',
status: 'active',
lastLogin: new Date('2025-01-09'),
tasksCompleted: 89
},
{
id: 3,
name: 'Carol Williams',
email: '[email protected]',
role: 'Manager',
status: 'inactive',
lastLogin: new Date('2024-12-15'),
tasksCompleted: 203
},
{
id: 4,
name: 'David Brown',
email: '[email protected]',
role: 'User',
status: 'pending',
lastLogin: new Date('2025-01-07'),
tasksCompleted: 45
},
{
id: 5,
name: 'Eve Davis',
email: '[email protected]',
role: 'User',
status: 'active',
lastLogin: new Date('2025-01-09'),
tasksCompleted: 156
},
// Add more sample data...
{
id: 6,
name: 'Frank Miller',
email: '[email protected]',
role: 'Admin',
status: 'active',
lastLogin: new Date('2025-01-08'),
tasksCompleted: 312
},
{
id: 7,
name: 'Grace Wilson',
email: '[email protected]',
role: 'Manager',
status: 'active',
lastLogin: new Date('2025-01-09'),
tasksCompleted: 178
},
{
id: 8,
name: 'Henry Moore',
email: '[email protected]',
role: 'User',
status: 'inactive',
lastLogin: new Date('2024-11-20'),
tasksCompleted: 67
},
{
id: 9,
name: 'Iris Taylor',
email: '[email protected]',
role: 'User',
status: 'active',
lastLogin: new Date('2025-01-09'),
tasksCompleted: 234
},
{
id: 10,
name: 'Jack Anderson',
email: '[email protected]',
role: 'Manager',
status: 'active',
lastLogin: new Date('2025-01-08'),
tasksCompleted: 189
}
];
// Filter state
searchQuery = '';
selectedRole: string = 'all';
selectedStatus: string = 'all';
// Sort state
sortColumn: SortColumn = 'name';
sortDirection: SortDirection = 'asc';
// Pagination state
currentPage = 1;
pageSize = 5;
// Selection state
selectedRows = new Set<number>();
// Loading state
isLoading = false;
// Computed: Filtered data
get filteredUsers(): User[] {
return this.allUsers.filter(user => {
// Search filter
const query = this.searchQuery.toLowerCase();
const matchesSearch = !query ||
user.name.toLowerCase().includes(query) ||
user.email.toLowerCase().includes(query);
// Role filter
const matchesRole = this.selectedRole === 'all' ||
user.role === this.selectedRole;
// Status filter
const matchesStatus = this.selectedStatus === 'all' ||
user.status === this.selectedStatus;
return matchesSearch && matchesRole && matchesStatus;
});
}
// Computed: Sorted data
get sortedUsers(): User[] {
const sorted = [...this.filteredUsers];
sorted.sort((a, b) => {
let aVal: any = a[this.sortColumn];
let bVal: any = b[this.sortColumn];
// Handle dates
if (aVal instanceof Date) {
aVal = aVal.getTime();
bVal = (bVal as Date).getTime();
}
// Handle strings (case-insensitive)
if (typeof aVal === 'string') {
aVal = aVal.toLowerCase();
bVal = bVal.toLowerCase();
}
if (aVal < bVal) return this.sortDirection === 'asc' ? -1 : 1;
if (aVal > bVal) return this.sortDirection === 'asc' ? 1 : -1;
return 0;
});
return sorted;
}
// Computed: Paginated data
get paginatedUsers(): User[] {
const start = (this.currentPage - 1) * this.pageSize;
const end = start + this.pageSize;
return this.sortedUsers.slice(start, end);
}
// Computed: Pagination info
get totalPages(): number {
return Math.ceil(this.sortedUsers.length / this.pageSize);
}
get totalResults(): number {
return this.sortedUsers.length;
}
get startResult(): number {
if (this.totalResults === 0) return 0;
return (this.currentPage - 1) * this.pageSize + 1;
}
get endResult(): number {
return Math.min(this.currentPage * this.pageSize, this.totalResults);
}
pageSizeChanged(newValue: number | string) {
const numeric = typeof newValue === 'string' ? Number(newValue) : newValue;
if (typeof numeric === 'number' && !Number.isNaN(numeric) && numeric !== this.pageSize) {
this.pageSize = numeric;
return;
}
this.currentPage = 1;
}
get pages(): number[] {
const pages: number[] = [];
const maxVisible = 5;
const half = Math.floor(maxVisible / 2);
let start = Math.max(1, this.currentPage - half);
let end = Math.min(this.totalPages, start + maxVisible - 1);
// Adjust start if we're near the end
if (end - start < maxVisible - 1) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
}
// Computed: Selection state
get allPageSelected(): boolean {
if (this.paginatedUsers.length === 0) return false;
return this.paginatedUsers.every(user => this.selectedRows.has(user.id));
}
get somePageSelected(): boolean {
if (this.paginatedUsers.length === 0) return false;
return this.paginatedUsers.some(user => this.selectedRows.has(user.id)) &&
!this.allPageSelected;
}
// Actions
sort(column: SortColumn) {
if (this.sortColumn === column) {
// Toggle direction
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
} else {
// New column, default to ascending
this.sortColumn = column;
this.sortDirection = 'asc';
}
}
goToPage(page: number) {
if (page < 1 || page > this.totalPages) return;
this.currentPage = page;
}
nextPage() {
this.goToPage(this.currentPage + 1);
}
previousPage() {
this.goToPage(this.currentPage - 1);
}
toggleAllPageSelection() {
if (this.allPageSelected) {
// Deselect all on page
this.paginatedUsers.forEach(user => this.selectedRows.delete(user.id));
} else {
// Select all on page
this.paginatedUsers.forEach(user => this.selectedRows.add(user.id));
}
}
clearSelection() {
this.selectedRows.clear();
}
deleteSelected() {
if (this.selectedRows.size === 0) return;
const confirmed = confirm(`Delete ${this.selectedRows.size} user(s)?`);
if (!confirmed) return;
// Remove selected users
this.allUsers = this.allUsers.filter(user => !this.selectedRows.has(user.id));
// Clear selection
this.selectedRows.clear();
// Adjust page if needed
if (this.currentPage > this.totalPages && this.totalPages > 0) {
this.currentPage = this.totalPages;
}
}
// Reset filters
resetFilters() {
this.searchQuery = '';
this.selectedRole = 'all';
this.selectedStatus = 'all';
this.currentPage = 1;
}
// Watch for filter changes and reset to page 1
searchQueryChanged() {
this.currentPage = 1;
}
selectedRoleChanged() {
this.currentPage = 1;
}
selectedStatusChanged() {
this.currentPage = 1;
}
}Template (data-table.html)
Styles (data-table.css)
How It Works
Filtering Pipeline
Sorting
Pagination
Selection
Performance
Variations
Server-Side Pagination
Inline Editing
Column Visibility Toggle
Related
Last updated
Was this helpful?