Cannot reinitialise DataTable with Angular Datatable

Viewed 8643

I have a Angular application with a simple CRUD functionality. I have tested my data with a static HTML table and this works. Now I am using a data table framework called Angular data table.

Link: https://l-lin.github.io/angular-datatables/#/welcome

I can Create, Read and Delete a record but after this action I get a error like:

DataTables warning: table id=DataTables_Table_0 - Cannot reinitialise DataTable. For more information about this error, please see http://datatables.net/tn/3

I have tried several solutions like the link it provided and other stack overflows post like this:

Getting error in Angular 5 Datatables: cannot reinitialise DataTable

https://l-lin.github.io/angular-datatables/#/basic/angular-way

https://l-lin.github.io/angular-datatables/#/advanced/rerender

This is my code in car-component.js and I use a car-services.js for all my HTTP calls.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { CarService } from '../services/carservices/car.service';
import { CarVM } from '../viewmodels/car/car-vm';
import { Subject } from 'rxjs';
import { DataTableDirective } from 'angular-datatables';

@Component({
  selector: 'app-car',
  templateUrl: './car.component.html',
  styleUrls: ['./car.component.css']
})
export class CarComponent implements OnInit {

  FormCar: any;
  countrecords: any;
  Carid: number = 0;
  dtTrigger: Subject<any> = new Subject();
  dtElement: DataTableDirective;
  allCars: CarVM[];
  dtOptions: DataTables.Settings = {};

  constructor(private formbuilder: FormBuilder, private carservice: CarService) { }

  ngOnInit() {

    this.dtOptions = {
      pagingType: 'full_numbers',
      pageLength: 5,
    };

    this.GetCar();
    this.GetCarCount();

    this.FormCar = this.formbuilder.group({
      CarId: ['', Validators.required],
      Brand: ['', Validators.required],
      Model: ['', Validators.required],
      Color: ['', Validators.required],
      TopSpeed: ['', Validators.required],
    });
  }

  AddCar(CarVM: CarVM) {
    CarVM.CarId = this.Carid;
    this.carservice.CreateCar(CarVM).subscribe(() => {
      this.GetCar();
      this.GetCarCount();
      this.Reset();
      this.Carid = 0;
    })
  }

  GetCar() {
    this.carservice.getAllCars().subscribe(res => {
      this.allCars = res;
      this.dtTrigger.next();
    })
  }

  GetCarCount() {
    this.countrecords = this.carservice.getCount();
  }

  EditCar(car: CarVM) {
    this.carservice.updateCar(car).subscribe(Response => {
      this.Carid = Response.CarId;
      this.FormCar.controls['Brand'].setValue(Response.Brand);
      this.FormCar.controls['Model'].setValue(Response.Model);
      this.FormCar.controls['Color'].setValue(Response.Color);
      this.FormCar.controls['TopSpeed'].setValue(Response.TopSpeed);
    })
  }

  DeleteCar(CarId: string) {
    if (confirm("Are you sure?")) {
      this.carservice.deleteCar(CarId).subscribe(() => {
        this.GetCar();
        this.GetCarCount();
      })
    }
  }

  Reset() {
    this.FormCar.reset();
  }
}

And this is my HTML page from car:

<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="row-border hover">
  <thead>
    <tr>
      <th>Id</th>
      <th>Brand</th>
      <th>Model</th>
      <th>Color</th>
      <th>Speed</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let car of allCars">
      <td>{{car.carId}}</td>
      <td>{{car.brand}}</td>
      <td>{{car.model}}</td>
      <td>{{car.color}}</td>
      <td>{{car.topSpeed }}</td>
      <td>
        <button type="button" class="btn btn-danger mr-1" (click)="DeleteCar(car.carId)">Delete</button>
      </td>
    </tr>
  </tbody>
</table>

NOTE:

I used the Angular version not the JQuery version!

Can someone point me in the right direction?

3 Answers

You need to destroy datatable instance before triggering it again.

You can create a function like this:

import { DataTableDirective } from 'angular-datatables';

dtElement: DataTableDirective;

isDtInitialized:boolean = false

  GetCar() {
    this.carservice.getAllCars().subscribe(res => {
      this.allCars = res;

      if (this.isDtInitialized) {
        this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
          dtInstance.destroy();
          this.dtTrigger.next();
        });
      } else {
        this.isDtInitialized = true
        this.dtTrigger.next();
      }
    })
  }

Using this, for the first time it will go to the else block and trigger datatable directly. After that when you refresh, it will first destroy the datatable and then trigger.

The above answer @Adrita Sharma was not working for me, it was throwing this error.

core.js:9110 ERROR TypeError: Cannot read property 'dtInstance' of undefined

So I added

@ViewChild(DataTableDirective, {static: false})

and it worked

import { Component, ViewChild, OnInit } from '@angular/core';
import { DataTableDirective } from 'angular-datatables';

export class AttendancePage implements OnInit {

@ViewChild(DataTableDirective, {static: false})
dtElement: DataTableDirective;

isDtInitialized:boolean = false;

And inside function where i initialized my datatable

if (this.isDtInitialized) {
    this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
        dtInstance.destroy();
        this.dtTrigger.next();
    });
} else {
    this.isDtInitialized = true;
    this.dtTrigger.next();
}

HTML

<div class="table-responsive">
                                            <table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" 
                                                style="width: 99.5% !important;" class="table table-responsive-sm table-bordered table-striped table-sm">
                                            <thead>
                                                <tr>
                                                <th>Client ID</th>
                                                <th>Client Name</th>
                                                <th>Client Phone</th>
                                                <th>Join Date</th>
                                                <th>Email</th>
                                                <th>Is Active</th>
                                                <th>Operation</th>
                                                </tr>
                                            </thead>
                                            <tbody *ngIf="modelList && modelList?.length != 0">
                                                <tr *ngFor="let m of modelList">
                                                <td>{{ m.ClientID }}</td>
                                                <td>{{ m.ClientName }}</td>
                                                <td>{{ m.ClientPhone }}</td>
                                                <td>{{ m.JoinDate | date : "MMM d, y" }}</td>
                                                <td>{{ m.Email}}</td>
                                                <td>
                                                        <div class="btn-group project-list-ad" *ngIf="m.IsActive">
                                                                <button class="btn btn-white btn-xs">Active</button>
                                                        </div>

                                                        <div class="btn-group project-list-ad-rd" *ngIf="!m.IsActive">
                                                                <button class="btn btn-white btn-xs">Unactive</button>
                                                        </div>
                                                </td>
                                                <td>
                                                    <button class="btn btn-pill btn-dark" type="button" (click)='editbuttonClick(m.ClientID)'>
                                                    <i class="fa fa-file-o fa-sm mt-1"></i>&nbsp;Edit
                                                    </button>
                                                </td>
                                                </tr>
                                            </tbody>
                                            <tbody *ngIf="!modelList || modelList?.length == 0">
                                                <tr>
                                                <td colspan="3" class="no-data-available">No data!</td>
                                                </tr>
                                            <tbody>
                                            </table>
                                        </div>

Type Script Code For Angular 8

@ViewChild(DataTableDirective, {static : false})
dtElement: DataTableDirective;

dtOptions: DataTables.Settings = {};
dtTrigger: Subject<ClientList> = new Subject();

implements AfterViewInit on classs

loadAllData() {

const that = this;


this.dtOptions = {
  pagingType: 'full_numbers',
  pageLength: 10,
  serverSide: true,
  processing: true,
  ajax: (dataTablesParameters: any, callback) => {
    that._http
      .post<DataTablesResponse>(
        //'https://angular-datatables-demo-server.herokuapp.com/',
        this.env.apiUrl + 'api/setup/ClientList_SelectAll_Grid',
        dataTablesParameters, {}
      ).subscribe(resp => {
        //console.log(dataTablesParameters);
        that.modelList = resp.data;


        callback({
          recordsTotal: resp.recordsTotal,
          recordsFiltered: resp.recordsFiltered,
          data: []
        });
      });
  },
  columns: [{ data: 'ClientID' }, { data: 'ClientName' }, { data: 'ClientPhone' }, { data: 'JoinDate' }, { data: 'Email' }, { data: 'IsActive' }, { defaultContent: '', orderable: false }]
};



 }

 ngAfterViewInit(): void {
    this.dtTrigger.next();
 }



 rerender(): void {

this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
  // Destroy the table first
  dtInstance.destroy();
  // Call the dtTrigger to rerender again
  this.dtTrigger.next();
});


}

full example of Angular DataTables Server side pagination & Sorting

Related