l-lin / angular-datatables

DataTables with Angular
https://l-lin.github.io/angular-datatables/
MIT License
1.57k stars 481 forks source link

Rerender datatable in angular way, Default Sorting, pagination, search is not working #1693

Closed thanjeys closed 1 year ago

thanjeys commented 1 year ago

:beetle: bug report

without rerender Angularway is working good. after Rerender datatable integration. Default Sorting, pagination, search is not working

I followed this https://l-lin.github.io/angular-datatables/#/basic/angular-way then https://l-lin.github.io/angular-datatables/#/advanced/rerender

Is rerender possible with angular way ?

:microscope: Minimal Reproduction

StackBlitz/GitHub Link:

https://github.com/thanjeys/angular-datatable-rerender/tree/main/src/app/components/myproducts

Step-by-step Instructions:

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

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

:8ball: Expected behavior

A clear and concise description of what you expected to happen.

:camera: Screenshots

image

getting empty when sorting,search image

:globe_with_meridians: Your Environment

:memo: Additional context

thanjeys commented 1 year ago

Hi, It seems you're using v10 of angular-datatables for Angular v13 project. Can you run npm install angular-datatables@13.1.0 and try again?

I tried, still am getting same issue. Is there any demo or Stack url ?

Angularway rerender ?

Thanks

thanjeys commented 1 year ago

Any body there to help ?

I reload getdata() Method. After create product. But all the records showing datatable ?

Is there any solution ? Pls help me on this.

import { Component, OnDestroy, OnInit, AfterViewInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs';
import { MatDialogConfig, MatDialog } from '@angular/material/dialog';
import { ProductCreateComponent } from '../products/product-create/product-create.component';
import {DataTableDirective, DataTablesModule} from 'angular-datatables';

const dialogConfig= new MatDialogConfig();
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;

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

  dtOptions: DataTables.Settings = {};
  dtElement: DataTableDirective;

  persons: any[] = [];
  isDtInitialized:boolean = false;

  // We use this trigger because fetching the list of persons can be quite long,
  // thus we ensure the data is fetched before rendering
  dtTrigger: Subject<any> = new Subject<any>();

  constructor(
    private httpClient: HttpClient,
    private dialog:MatDialog,
    ) { }

  ngOnInit(): void {
    this.dtOptions = {
      pagingType: 'full_numbers',
      pageLength: 15
    };
   this.getData();
  }

  createProduct()
  {
    dialogConfig.width = "60%";
    dialogConfig.data = {
      // updateProdId: this.updateProductId
    }
    this.dialog.open(ProductCreateComponent, dialogConfig);

    this.dialog.afterAllClosed.subscribe(e=>{
      this.getData();
    });
  }

  getData() {
    this.httpClient.get<any[]>('https://api.npoint.io/73d6b5477838c422e54c')
    .subscribe(data => {
      this.persons = (data as any).data;
      // Calling the DT trigger to manually render the table
      if (this.isDtInitialized) {
        this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
          dtInstance.destroy();
          this.dtTrigger.next(this.dtOptions);
        });
      } else {
        this.isDtInitialized = true
        this.dtTrigger.next(this.dtOptions);
      }
    });
  }

  ngOnDestroy(): void {
    // Do not forget to unsubscribe the event
    this.dtTrigger.unsubscribe();
  }

  update(name) {
    this.getData();
  }
}
<button (click)="createProduct()" mat-raised-button>New Product</button>
<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="row-border hover">
    <thead>
      <tr>
        <th>ID</th>
        <th>First name</th>
        <th>Last name</th>
        <th></th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let person of persons">
        <td>{{ person.id }}</td>
        <td>{{ person.firstName }}</td>
        <td>{{ person.lastName }}</td>
        <td><a href="#" (click)="update(person.firstName)">Edit</a></td>
      </tr>
    </tbody>
  </table>

image

shanmukhateja commented 1 year ago

There is a flaw when using Angular way. In the AJAX callback, we send data: [] to datatables.net library which indicates there is no data to render. (In my experience, it is recommended to always let the library usually render the HTML)

However, by using ngFor to render the data, we're "forcing" it. So, although the library's internal state has no data, it still another part of the code (HTML renderer logic) updates DOM accordingly.

This has caused a lot of headache for one of my projects in the past and so, I introduced ngPipe and ngTemplateRef option.

Please take a look at following examples and adapt your code accordingly.

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

https://l-lin.github.io/angular-datatables/#/advanced/using-template-ref

I am closing this issue now as they are a much better way to work with the library.

Moving forward, I plan on clearly marking the Angular way examples as "Not recommended" and instead push for the above mentioned links.

thanjeys commented 1 year ago

Hi,

Thanks for your response. But i solved this issue. Now its works perfectly.

component.ts file

import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { DataTableDirective, DataTablesModule } from 'angular-datatables';
import { EmployeeService } from 'src/app/__services/admin/employee.service';
import { CreateEmployeeComponent } from '../create-employee/create-employee.component';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzMessageService } from 'ng-zorro-antd/message';

interface Employee {
  id: number;
  name: string;
  email: string;
  mobile: number;
  role_name: string;
  products?: string;
  status: boolean;
}

@Component({
  selector: 'app-employes-table',
  templateUrl: './employes-table.component.html',
  styleUrls: ['./employes-table.component.scss'],
})
export class EmployesTableComponent implements OnDestroy, OnInit {
  @ViewChild(DataTableDirective, { static: false })
  dtElement!: DataTableDirective;
  dtOptions: DataTables.Settings = {};
  dtTrigger: Subject<any> = new Subject<any>();
  isDtInitialized: boolean = false;

  persons: any[] = [];

  loading: boolean = true;
  updateEmpId!: number;
  listOfEmployees: Employee[] = [];

  constructor(
    private empService: EmployeeService,
    private modalService: NzModalService,
    private message: NzMessageService
  ) {}

  ngOnInit(): void {
    this.dtOptions = {
      pagingType: 'full_numbers',
      pageLength: 10,
      processing: true,
    };
    this.getEmployees();
  }

  getEmployees() {
    this.loading = true;
    this.empService.getAll().subscribe((res) => {
      this.listOfEmployees = res.data;
      if (this.isDtInitialized) {
        this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
          dtInstance.destroy();
          this.dtTrigger.next(null);
        });
      } else {
        this.isDtInitialized = true;
        this.dtTrigger.next(null);
      }
      this.loading = false;
    });
  }

  openEmployeeModal(updateEmpId?: number): void {
    const modal = this.modalService.create({
      nzTitle: updateEmpId ? 'Update Employee' : 'New Employee',
      nzContent: CreateEmployeeComponent,
      nzClassName: 'defaultModal',
      nzMaskClosable: false,
      nzComponentParams: {
        updateEmpId: updateEmpId,
      },
    });
    modal.afterOpen.subscribe(() => console.log('[afterOpen] emitted!'));
    modal.afterClose.subscribe((result) => {
      if (result) this.getEmployees();
      console.log('[afterClose] The result is:', result);
    });
  }

  updateStatus(empId: number, status: boolean) {
    const formData = {
      status: !status,
    };
    this.empService.updateStatus(empId, formData).subscribe({
      next: (res: any) => {
        this.message.create('success', res.message);
        console.log(res);
      },
      error: (err: any) => {
        this.message.create('error', 'Failed to Update Status');
        console.log(err.error);
      },
    });
  }

  ngOnDestroy(): void {
    this.dtTrigger.unsubscribe();
  }

  display(products: any) {
        return products.join(', ');
  }
}

component.html

<nz-spin [nzSpinning]="loading" [nzSize]="'large'">

<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="row-border hover">
        <thead *ngIf="!loading">
          <tr>
            <th>ID</th>
            <th>First names</th>
            <th>Last name</th>
            <th>Last name</th>
            <th>Last name</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let product of listOfEmployees">
                <td>{{product.name}}</td>
                <td>{{product.email}}</td>
                <td>{{product.mobile}}</td>
                <td>{{product.role_name}}</td>
                <td>{{display(product.products)}}</td>
                <td><nz-switch [(ngModel)]="product.status" (click)="updateStatus(product.id, product.status)"></nz-switch>
                  <button pButton pRipple icon="pi pi-pencil" style="margin-left:20px ;" class="p-button-rounded p-button-outlined p-button-secondary mr-2" (click)="openEmployeeModal(product.id)"></button>
                </td>
          </tr>
        </tbody>
      </table>
</nz-spin>

Thank you