Topic: Angular DataTable Pagination issue
Expected behavior When selecting records with a mdb-checkbox, the checked value should persist between pagination
Actual behavior when you select a checkbox and navigate away to the the next page, then back again, the check box looses it state.
Resources (screenshots, code snippets etc.)
Grid.Component.ts
import { Component, OnInit, HostListener, AfterViewInit, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';
import { BillingService } from '../Services/billing.service';
import { FormControl, FormGroup } from '@angular/forms';
import { MdbTableService, WavesModule, TableModule, InputsModule, MdbTableDirective, MdbTablePaginationComponent } from 'ng-uikit-pro-standard';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { BillingRecord } from '../Models/billingRecord';
import { forEach } from '@angular/router/src/utils/collection';
@Component({
selector: 'app-billing-grid',
templateUrl: './billing-grid.component.html',
styleUrls: ['./billing-grid.component.scss']
})
export class BillingGridComponent implements OnInit, AfterViewInit {
@ViewChild(MdbTableDirective) mdbTable: MdbTableDirective;
@ViewChild(MdbTablePaginationComponent) mdbTablePagination: MdbTablePaginationComponent;
@ViewChild('row') row: ElementRef;
constructor(private billingService: BillingService,
private tableService: MdbTableService,
private cdRef: ChangeDetectorRef,
private http: HttpClient) { }
// Search Function variables
searchText: string = '';
previous: string;
maxVisibleItems: number = 10;
firstItemIndex: any;
lastItemIndex: any;
// Dataset from API
private billingRecords: any[];
// Dataset going to API
private doNotBillRecords: any[] = [];
// Table Header data
private headElements: string[] = [
'Project Id',
'Project Name',
'Region',
'Client Matter Number',
'Project Owner',
'TimeKeeper Id',
'Days Idle',
'Document Count',
'Per Document Rate',
'Total Cost',
'Bill Status',
'DO NOT BILL'
]
ngOnInit() {
this.getAllBillingRecords();
}
ngAfterViewInit() {
this.mdbTablePagination.setMaxVisibleItemsNumberTo(this.maxVisibleItems);
this.firstItemIndex = this.mdbTablePagination.firstItemIndex;
this.lastItemIndex = this.mdbTablePagination.lastItemIndex;
this.mdbTablePagination.calculateFirstItemIndex();
this.mdbTablePagination.calculateLastItemIndex();
this.cdRef.detectChanges();
}
onNextPageClick(data: any) {
this.firstItemIndex = data.first;
this.lastItemIndex = data.last;
}
onPreviousPageClick(data: any) {
this.firstItemIndex = data.first;
this.lastItemIndex = data.last;
}
// Fetches billing records from api and stashes them into array for in memory
getAllBillingRecords() {
this.billingService.getAllBillingRecords()
.subscribe((data: any) => {
data.forEach((record: any) => {
this.billingRecords.push({
projectId: record.projectId.toString(),
projectName: record.projectName,
region: record.region,
clientMatterNumber: record.clientMatterNumber,
projectOwner: record.projectOwner,
tkid: record.tkid,
numberOfDaysIdle: record.numberOfDaysIdle,
documentCount: record.documentCount,
perDocumentRate: record.perDocumentRate,
totalCost: record.totalCost,
billStatus: record.billStatus
});
});
this.tableService.setDataSource(this.billingRecords);
});
this.billingRecords = this.tableService.getDataSource();
this.previous = this.tableService.getDataSource();
}
searchRecords() {
const prev = this.tableService.getDataSource();
if (!this.searchText) {
this.tableService.setDataSource(this.previous);
this.billingRecords = this.tableService.getDataSource();
}
if (this.searchText) {
this.billingRecords = this.tableService.searchLocalDataBy(this.searchText);
this.tableService.setDataSource(prev);
}
this.mdbTablePagination.calculateFirstItemIndex();
this.mdbTablePagination.calculateLastItemIndex();
this.tableService.searchDataObservable(this.searchText).subscribe((data: any) => {
if (data.length === 0) {
this.firstItemIndex = 0;
}
if (this.tableService.getDataSource().length !== data.length) {
this.firstItemIndex = 1;
this.lastItemIndex = this.maxVisibleItems;
}
this.mdbTablePagination.calculateFirstItemIndex();
this.mdbTablePagination.calculateLastItemIndex();
});
}
doNotBill(i) {
var index = this.doNotBillRecords.indexOf(i);
if (index === -1) {
this.doNotBillRecords.push(i);
} else {
this.doNotBillRecords.splice(index, 1);
}
}
findAndUpdate() {
for (var i = 0; i < this.doNotBillRecords.length; i++) {
if (this.doNotBillRecords[i].billStatus === 'BILL') {
this.doNotBillRecords[i].billStatus = 'DO NOT BILL'
}
}
}
updateBilling() {
this.findAndUpdate();
this.billingService.updateBillingRecords(this.doNotBillRecords).subscribe();
}
}
Grid.HTML
<div class="container">
<table mdbTable striped="true" small="true">
<thead class="sticky-top klgDarkBlue white-text">
<tr>
<th *ngFor="let head of headElements; let i = index" [mdbTableSort]="elements" [sortBy]="headElements[i]" scope="col">{{ head }} </th>
</tr>
</thead>
<tbody #row>
<tr mdTablecol (rowCreated)="onRowCreate($event)" (rowRemoved)="onRowRemove($event)" *ngFor="let record of billingRecords; let i = index">
<th *ngIf="i+1 >= firstItemIndex && i < lastItemIndex" scope="row">{{ record.projectId}}</th>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.projectName }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.region }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.clientMatterNumber }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.projectOwner }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.tkid }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.numberOfDaysIdle }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.documentCount }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.perDocumentRate | currency }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.totalCost | currency }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">{{ record.billStatus }}</td>
<td *ngIf="i+1 >= firstItemIndex && i < lastItemIndex">
<mdb-checkbox (change)="doNotBill(record)"></mdb-checkbox>
</td>
</tr>
</tbody>
<tfoot class="grey lighten-5 w-100">
<tr>
<td colspan="8">
<mdb-table-pagination paginationAlign="" [searchDataSource]="billingRecords" (nextPageClick)="onNextPageClick($event)"
(previousPageClick)="onPreviousPageClick($event)">
</mdb-table-pagination>
</td>
<td colspan="4">
<button mdbBtn type="button" (click)="updateBilling()" color="primary" outline="true" mdbWavesEffect>Update Billing</button>
</td>
</tr>
</tfoot>
</table>
</div>
Damian Gemza staff answered 6 years ago
Dear James,
Please take a look at the below code (i have modified only the *ngFor on tr
elements (removed *ngIf
and replaced it with [ngClass]
). Now it works fine.
.html:
<div class="container">
<div class="row">
<div class="col-md-6 mx-auto">
<div class="md-form">
<input type="text" class="form-control" [(ngModel)]="searchText" (keyup)="searchItems()" id="search-input"
mdbInput>
<label for="search-input">Search</label>
</div>
</div>
<table mdbTable stickyHeader="true" hover="true" striped="true" class="z-depth-1">
<thead class="sticky-top">
<tr>
<th *ngFor="let head of headElements; let i = index" [mdbTableSort]="elements" [sortBy]="headElements[i]"
scope="col">{{head}} <mdb-icon fas icon="sort"></mdb-icon>
</th>
</tr>
</thead>
<tbody #row>
<tr mdbTableCol (rowCreated)="onRowCreate($event)" (rowRemoved)="onRowRemove($event)" *ngFor="let el of elements; let i = index">
<th [ngClass]="{'d-none': !(i+1 >= firstItemIndex && i < lastItemIndex)}" scope="row">
<mdb-checkbox [default]="true"></mdb-checkbox>
{{el.id}}
</th>
<td [ngClass]="{'d-none': !(i+1 >= firstItemIndex && i < lastItemIndex)}" class="red-text">{{el.first}}</td>
<td [ngClass]="{'d-none': !(i+1 >= firstItemIndex && i < lastItemIndex)}">{{el.last}}</td>
<td [ngClass]="{'d-none': !(i+1 >= firstItemIndex && i < lastItemIndex)}">{{el.handle}}</td>
</tr>
</tbody>
<tfoot class="grey lighten-5 w-100">
<tr>
<td colspan="4">
<mdb-table-pagination paginationAlign="" [searchDataSource]="elements" (nextPageClick)="onNextPageClick($event)"
(previousPageClick)="onPreviousPageClick($event)"></mdb-table-pagination>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
Best Regards,
Damian
Damian Gemza staff answered 6 years ago
Dear James,
That's the correct behavior because we're rendering the table row's with a mix of two directives: *ngFor
and *ngIf
. ngFor is iterating through your data array to render items, and ngIf is checking if element should be visible in DOM or not.
A possible workaround for this situation is not to use *ngIf
to dynamically render or not items, but use the [ngClass]
to add a .d-block
class, whenever the table row is not needed to be visible.
Best Regards,
Damian
JamesR commented 6 years ago
Damian,
can you please provide a code snip on what to change based on your above reccomendation?
thanks
FREE CONSULTATION
Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.
Resolved
- User: Free
- Premium support: No
- Technology: MDB Angular
- MDB Version: 7.4.3
- Device: Windows
- Browser: chrome
- OS: Windows 10
- Provided sample code: No
- Provided link: No