l-lin / angular-datatables

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

Issues when using Angular Datatables as a shared component #1471

Closed alwinaugustin closed 3 years ago

alwinaugustin commented 4 years ago

I am using the Datatable as a component so that I can use it in all the templates I have. I have the following files :

table.component.ts


import * as _ from "lodash";
import { Observable, Subject, merge } from 'rxjs';
import { Component, OnInit, ViewChild, Renderer2, Input, Output, EventEmitter, ContentChild, ElementRef } from "@angular/core";

import { DataTableDirective } from "angular-datatables";
import { TableActionEvent } from "../../types/table/table.actions";

@Component({
    selector: "common-table",
    templateUrl: "./table.component.html",
    styleUrls: ["./table.component.scss"],
})
export class TableComponent implements OnInit {

    public dtOptions: DataTables.Settings = {};
    public dtTrigger: Subject<any> = new Subject();
    public _data: Array<any>;
    public _settings: any;
    public _loading: boolean;

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

    /**
    *  Grid data with pagination meta
    */

    @Input()

    set data(data: any) {
        if (data) {
            this._data = data;
        }
    }

    get data(): any {
        return this._data;
    }

    /**
    *  Grid data with pagination meta
    */

    @Input()

    set settings(data: any) {
        if (data) {
            this._settings = data;
        }
    }

    get settings(): any {
        return this._settings;
    }

    @Input()

    set loading(loading: boolean) {
        this._loading = loading;
        setTimeout(() => {
            $('#releaseDatatable').DataTable().destroy();
            this.dtTrigger.next();
        }, 100);
    }

    get loading(): boolean {
        return this._loading;
    }

    @Output() public event: EventEmitter<TableActionEvent<any>> = new EventEmitter<TableActionEvent<any>>();

    /**
     * DashboardComponent constructor
     */
    constructor(
        private renderer: Renderer2,
        private elem: ElementRef
    ) { }

    /**
     * @memberof DashboardComponent
     */
    public ngOnInit(): void {
        this.dtOptions = {
            info: false,
            order: []
        };
    }

    /**
     *
    */
    cellRender(render, row) {
        if (_.isString(render)) {
            return render;
        }

        if (_.isFunction(render)) {
            return render(row);
        }
    }

    /**
     * @param $event
     */
    public search($event) {
        const term = $event.target.value;
        this.datatableElement.dtInstance.then((dtInstance: DataTables.Api) => {
            dtInstance.search(term).draw();
        });
    }

    /**
   * On Table Action
   *
   * @param {string} action
   * @param {T} row
   * @param {number} index
   *
   * @memberof AdminGridComponent
   */
    public onAction(action: string, row: any): void {
        const tableActionItem: any = {
            action: action,
            row: row
        };

        this.event.emit(tableActionItem);
    }

    rerender() {
        this.datatableElement.dtInstance.then((dtInstance: DataTables.Api) => {
            dtInstance.destroy();
            this.dtTrigger.next();
        });
    }

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

and the template file table.component.html is like the following:


<div class="ma-datatables">
    <form>
        <div class="d-flex align-items-center app-login_input-layout">
            <input class="form-control app-login_input-style rm-release_search-bar" type="search" id="release-search"
                placeholder="Search here..." (keyup)=search($event)>
            <i class="material-icons rm-release_search-icon">search</i>
        </div>
    </form>
    <div class="table-responsive">
        <div *ngIf="!loading">
            <table class="table animate__animated animate__fadeIn" id="releaseDatatable" datatable
                [dtOptions]="dtOptions" [dtTrigger]="dtTrigger">
                <thead>
                    <tr>
                        <th *ngFor="let header of settings.columns">{{ header.title }}</th>
                        <th *ngIf="settings.actions">Actions</th>
                    </tr>
                </thead>
                <tbody>
                    <tr *ngFor="let release of data">
                        <ng-container *ngFor="let column of settings.columns">
                            <td class="rm-release_table-cell" [style.width]=column.width>
                                <div>
                                    <p *ngIf="column.dataType !='html'" class="rm-release_data-id">
                                        {{release[column.field]}}
                                    </p>
                                    <span *ngIf="column.dataType =='html'">
                                        <div innerHTML="{{ cellRender(column.render, release) }}"></div>
                                    </span>
                                </div>
                            </td>
                        </ng-container>
                        <td class="rm-release_table-cell" *ngIf="settings.actions">
                            <div>
                                <a id="release-action" href="javascript:void(0)" (click)="openAction($event)">
                                    <i class="material-icons">more_vert</i>
                                </a>
                                <div class="rm-release_action-layout hidden" #actionSlide
                                    id="release-{{release[settings.columns[0].field]}}">
                                    <div class="rm-release_action-container animate__animated" #actionContainer>
                                        <div
                                            class="d-flex align-items-center justify-content-between rm-release_action-header">
                                            <a class="rm-release_action-close" href="javascript:void(0)"
                                                (click)="closeAction($event)">
                                                <i class="material-icons">close</i>
                                            </a>
                                        </div>
                                        <div class="rm-release_action-item-layout">
                                            <div class="rm-release_action-item-layout">
                                                <ng-container *ngIf="release.permitted_actions;else actionMenu">
                                                    <ng-container *ngFor="let action of settings.actions">
                                                        <div *ngIf="release.permitted_actions.includes(action.action)"
                                                            class="d-flex align-items-center rm-release_action-item"
                                                            (click)="onAction(action.action, release)">
                                                            <i class="material-icons">{{action.icon}}</i>
                                                            <p>{{action.title}}</p>
                                                        </div>
                                                    </ng-container>
                                                </ng-container>
                                                <ng-template #actionMenu>
                                                    <ng-container *ngFor="let action of settings.actions">
                                                        <div class="d-flex align-items-center rm-release_action-item"
                                                            (click)="onAction(action.action, release)">
                                                            <i class="material-icons">{{action.icon}}</i>
                                                            <p>{{action.title}}</p>
                                                        </div>
                                                    </ng-container>
                                                </ng-template>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>

    </div>
</div>

and I am using this component as shown below:

  <common-table [data]="items" [settings]="settings" [loading]="isLoading"
      (event)="onGridEvent($event)">
  </common-table>

Now I am facing multiple issues here :

It is showing No data available in table sometimes, but when the page is refreshed, it will be gone.

Sometimes, it is showing DataTables warning: table id=releaseDatatable - Cannot reinitialise DataTable. For more information about this error, please see http://datatables.net/tn/3"

I'm submitting a...

[ ] Bug report [ ] Feature request [ ] Documentation issue or request [ x ] Question

Environment


- node version: 12.13.1
- angular version: 9
- angular-cli version: 9.1.10
- jquery version: 3.5.1
- datatables version:1.10.21
- angular-datatables version: 9.0.2
shanmukhateja commented 3 years ago

It is showing No data available in table sometimes, but when the page is refreshed, it will be gone.

This could be dtTrigger misbehaving which is fixed when the page is refreshed.

Sometimes, it is showing DataTables warning: table id=releaseDatatable - Cannot reinitialise DataTable. For more information about this error, please see http://datatables.net/tn/3"

This is triggered when table which is already a DataTable ready being re-initialized as a DataTable. The URL mentioned on the alert talks about this more.

One solution is to use DT destroy() [API](https://datatables.net/reference/api/destroy()) to remove its enhancements so you can re-initialize your HTML table with new dtOptions. In other words, call destroy() on ngOnDestroy of TableComponent.

Also, your TableComponent isn't implementing OnDestroy which means it won't call ngOnDestroy.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 3 years ago

This issue has been closed due to inactivity. Please feel free to re-open the issue to add new inputs.