l-lin / angular-datatables

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

Issues with DtTrigger when rendering multiple tables #1165

Closed raydawg2000 closed 3 years ago

raydawg2000 commented 6 years ago

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request

What versions you are using?


- node version: 6.11.4
- angular version: 5.1.2
- angular-cli version: 1.6.3
- jquery version: 3.2.1
- datatables version: 1.10.16
- angular-datatables version: 5.0.0

Current behavior

I am trying to use multiple tables in a single view and used this Issue for reference on setup https://github.com/l-lin/angular-datatables/issues/1088

I have a dtOptions and dtTrigger in an array, an instance for each table.

@ViewChildren(DataTableDirective)
dtElements: QueryList<DataTableDirective>;
dtOptions: DataTables.Settings[] = [];
dtTrigger: Subject<any>[] = [];

ngOnInit() {
    this.dtTrigger["new"] = new Subject<any>();
    this.dtOptions["new"] = { ... };
}

<table datatable [dtOptions]="dtOptions['new']" [dtTrigger]="dtTrigger['new']"></table>

The table does not render with options. If I remove dtTrigger from the table config then the table renders as expected with options. The issue is then I cannot rerender the table because I need to call

dtTrigger.next()

but dtTrigger does not exist if I don't include it in the table definition. If I drop the array of dtTriggers and just use a single instance of dtTrigger for testing purposes

dtTrigger: Subject<any> = new Subject<any>();

<table datatable [dtOptions]="dtOptions['new']" [dtTrigger]="dtTrigger"></table>

This also fails to render the table. So it appears there is something not working properly with using dtTrigger when using the recommended method of rendering multiple tables.

Expected behavior

Datatables should function normally, not crash when using DtTrigger

Minimal reproduction of the problem with instructions

This was a template provided from another post and i modified to show the issue. Removing the dtTrigger from the table definition allows for table rendering. Any attempt to use dtTrigger results in the table not rendering.

http://plnkr.co/edit/ntYDaywa9xZ81AQR2obP

l-lin commented 6 years ago

You need to wrap the dtTrigger['new'].next() with a setTimeout so that instruction is "part" of the NgZone:

setTimeout(() => {
  this.dtTrigger['new'].next();    
});

See this plnkr.

vsundesha commented 5 years ago

Worked for me ! Thank you !

You need to wrap the dtTrigger['new'].next() with a setTimeout so that instruction is "part" of the NgZone:

setTimeout(() => {
  this.dtTrigger['new'].next();    
});

See this plnkr.

mritunjaybs1510 commented 5 years ago

Hi I am using the Angular datatables. Multiple table on same page but displaying in two tabs. i have gone through the above discussion and followed the suggestions mentioned for adding the setTimeout(() => { this.dtTrigger['new'].next();
}); But this solutions is not worked for me.. Here is my code My TypeScript Code

import { Component, OnInit, Input, ViewChild, OnDestroy, AfterViewInit, EventEmitter, Output, ViewChildren } from '@angular/core';
import { QueryList } from '@angular/core';
import { DataTableDirective } from 'angular-datatables';
import { Subject } from 'rxjs/Subject';
import { TabDirective } from 'ngx-bootstrap/tabs';
import 'rxjs/add/operator/map';
@Component({
  selector: 'app-student-list',
  templateUrl: './student-list.component.html',
  styleUrls: ['./student-list.component.css']
})
export class StudentListComponent implements OnDestroy, OnInit, AfterViewInit {
  @Input('studentList') studentList: any;
  @Input('allottedStudentList') allottedStudentList: any;
  @Output('clickCheckBox') clickCheckBox  = new EventEmitter<any>();
  @Output('selectAllCheckBox') selectAllCheckBox  = new EventEmitter<any>();
  @ViewChildren(DataTableDirective) dtElements: QueryList<DataTableDirective>;
  // @ViewChild(DataTableDirective) dtElement: DataTableDirective;
  dtOptions: DataTables.Settings[] = [];
  // dtTrigger: any = new Subject();
  dtTrigger: Subject<any>[] = [];

  // dtTrigger1: any = new Subject();
  studentIdArray: any = [];
  constructor() { }

  ngOnInit() {
    this.dtOptions[0] = {
      paging: false,
      processing: true,
      search: true,
      searching: true,
      columnDefs: [ {
        'targets': 4,
        'orderable': false
        } ]
    };
    this.dtOptions[1] = {
      paging: false,
      processing: true,
      search: true,
      searching: true,
      columnDefs: [ {
        'targets': 4,
        'orderable': false
        } ]
    };
    this.dtTrigger['first'] = new Subject<any>();
    this.dtTrigger['second'] = new Subject<any>();
  }

  ngOnDestroy(): void {
    // Do not forget to unsubscribe the event
    this.dtTrigger['first'].unsubscribe();
    setTimeout(() => {
      this.dtTrigger['second'].next();
    });
    // this.dtTrigger1.unsubscribe();
  }

  ngAfterViewInit(): void {
    this.dtTrigger['first'].next();
    setTimeout(() => {
      this.dtTrigger['second'].next();
    });
    // this.dtTrigger1.next();
  }

  rerender(): void {
  this.dtElements.forEach((dtElement: DataTableDirective) => {
    dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
      dtInstance.destroy();
      this.dtTrigger['first'].next();
      setTimeout(() => {
        this.dtTrigger['second'].next();
      });

    });
  });
  }

  selectCheckBox(studentId: any, usn: any) {
    if ($('#select_' + studentId).is(':checked')) {
      this.studentIdArray.push(studentId + '-' + usn);
    } else {
      this.studentIdArray.splice(this.studentIdArray.indexOf(studentId + '-' + usn), 1);
    }

   this.clickCheckBox.emit(this.studentIdArray);

  }

  selectAll() {
    const that = this;
     if ($('#select_all').is(':checked')) {
      $('.select_student').each(function() {
        $(this).prop('checked', true);
          if ($(this).is(':checked')) {
            that.studentIdArray.push($(this).attr('student') + '-' + $(this).attr('student_usn'));
          } else {
            that.studentIdArray.splice(that.studentIdArray.indexOf($(this).attr('student') + '-' + $(this).attr('student_usn')), 1);
          }
      });
     } else {
      $('.select_student').each(function() {
        $(this).prop('checked', false);
          if ($(this).is(':checked')) {
            that.studentIdArray.push($(this).attr('student'));
          } else {
            that.studentIdArray.splice(that.studentIdArray.indexOf($(this).attr('student')), 1);
          }
      });
     }

     this.selectAllCheckBox.emit(this.studentIdArray);
    }

}

HTML CODE

table #allottedStudentGrid datatable [dtOptions]="dtOptions[1]" [dtTrigger]="dtTrigger['first']" id="allottedStudentGrid" class="allottedStudentGrid table table-bordered

table #allottedStudentGrid datatable [dtOptions]="dtOptions[1]" [dtTrigger]="dtTrigger['second']" id="allottedStudentGrid" class="allottedStudentGrid table table-bordered

Edit: Fixed formatting

mritunjaybs1510 commented 5 years ago

image

mritunjaybs1510 commented 5 years ago

Any Help will be greatly appreciated

shohag000 commented 5 years ago

Hi I am using the Angular datatables. Multiple table on same page but displaying in two tabs. i have gone through the above discussion and followed the suggestions mentioned for adding the setTimeout(() => { this.dtTrigger['new'].next(); }); But this solutions is not worked for me.. Here is my code My TypeScript Code import { Component, OnInit, Input, ViewChild, OnDestroy, AfterViewInit, EventEmitter, Output, ViewChildren } from '@angular/core'; import { QueryList } from '@angular/core'; import { DataTableDirective } from 'angular-datatables'; import { Subject } from 'rxjs/Subject'; import { TabDirective } from 'ngx-bootstrap/tabs'; import 'rxjs/add/operator/map'; @component({ selector: 'app-student-list', templateUrl: './student-list.component.html', styleUrls: ['./student-list.component.css'] }) export class StudentListComponent implements OnDestroy, OnInit, AfterViewInit { @input('studentList') studentList: any; @input('allottedStudentList') allottedStudentList: any; @output('clickCheckBox') clickCheckBox = new EventEmitter(); @output('selectAllCheckBox') selectAllCheckBox = new EventEmitter(); @ViewChildren(DataTableDirective) dtElements: QueryList; // @ViewChild(DataTableDirective) dtElement: DataTableDirective; dtOptions: DataTables.Settings[] = []; // dtTrigger: any = new Subject(); dtTrigger: Subject[] = [];

// dtTrigger1: any = new Subject(); studentIdArray: any = []; constructor() { }

ngOnInit() { this.dtOptions[0] = { paging: false, processing: true, search: true, searching: true, columnDefs: [ { 'targets': 4, 'orderable': false } ] }; this.dtOptions[1] = { paging: false, processing: true, search: true, searching: true, columnDefs: [ { 'targets': 4, 'orderable': false } ] }; this.dtTrigger['first'] = new Subject(); this.dtTrigger['second'] = new Subject(); }

ngOnDestroy(): void { // Do not forget to unsubscribe the event this.dtTrigger['first'].unsubscribe(); setTimeout(() => { this.dtTrigger['second'].next(); }); // this.dtTrigger1.unsubscribe(); }

ngAfterViewInit(): void { this.dtTrigger['first'].next(); setTimeout(() => { this.dtTrigger['second'].next(); }); // this.dtTrigger1.next(); }

rerender(): void { this.dtElements.forEach((dtElement: DataTableDirective) => { dtElement.dtInstance.then((dtInstance: DataTables.Api) => { dtInstance.destroy(); this.dtTrigger['first'].next(); setTimeout(() => { this.dtTrigger['second'].next(); });

});

}); }

selectCheckBox(studentId: any, usn: any) { if ($('#select_' + studentId).is(':checked')) { this.studentIdArray.push(studentId + '-' + usn); } else { this.studentIdArray.splice(this.studentIdArray.indexOf(studentId + '-' + usn), 1); }

this.clickCheckBox.emit(this.studentIdArray);

}

selectAll() { const that = this; if ($('#select_all').is(':checked')) { $('.select_student').each(function() { $(this).prop('checked', true); if ($(this).is(':checked')) { that.studentIdArray.push($(this).attr('student') + '-' + $(this).attr('student_usn')); } else { that.studentIdArray.splice(that.studentIdArray.indexOf($(this).attr('student') + '-' + $(this).attr('student_usn')), 1); } }); } else { $('.select_student').each(function() { $(this).prop('checked', false); if ($(this).is(':checked')) { that.studentIdArray.push($(this).attr('student')); } else { that.studentIdArray.splice(that.studentIdArray.indexOf($(this).attr('student')), 1); } }); }

 this.selectAllCheckBox.emit(this.studentIdArray);
}

}

HTML CODE table #allottedStudentGrid datatable [dtOptions]="dtOptions[1]" [dtTrigger]="dtTrigger['first']" id="allottedStudentGrid" class="allottedStudentGrid table table-bordered"

table #allottedStudentGrid datatable [dtOptions]="dtOptions[1]" [dtTrigger]="dtTrigger['second']" id="allottedStudentGrid" class="allottedStudentGrid table table-bordered

Hello, ive same issues in multiple data table in single page. Is that problem solved?

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.

yeshwantnila commented 3 years ago

Is this problem solved

joshi-anshul commented 3 years ago

@shanmukhateja

any solution?

shanmukhateja commented 3 years ago

Hi,

The plnkr link provided is proving difficult to work with even after downloading its contents as zip file. Please provide a new reproducible repo to work, preferably StackBlitz or a GitHub repo.

epiphanizer commented 3 years ago

Also running into something like this.

epiphanizer commented 3 years ago

I have <table [dataTable]> within an ngFor* loop and am running into this issue. Most of the examples I've researched are setup with @ViewChildren directives that reference multiple known tables in the markup... i.e.:

<table dataTable [dtOptions]="dtOptions1" [dtTrigger]="dtTrigger1">
  //table stuff
</table>
<table dataTable [dtOptions]="dtOptions2" [dtTrigger]="dtTrigger2">
   //table stuff
</table>

However... the way we have structured our app, an arbitrary amount of Table Singleton components (app-datatable-component) may be deployed by our loop.

with the internals of app-datatable-component's html looking like:

<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger">

Is there any documentation for using angular-datatables within such a loop? ViewChildren doesn't seem like it will help me because the datatable instances are within encapsulated components rather than living in the host component's markup.

What should I do in that use case? Haven't seen examples of this nature... leading to issues very similar to this thread, but I think my situation may be a tiny bit different.

shanmukhateja commented 3 years ago

Hi @epiphanizer could you provide a reproducible GitHub repo or similar? It would help a lot.

epiphanizer commented 3 years ago

Hi @shanmukhateja! I ended up figuring out my issue. I will say this for anybody else who runs into this.

We were using ngOnChanges to watch for changes and update the table. ngOnChanges is pretty active in its change-detection and was responding to way more changes than we needed... I had to make sure we were setting appropriate conditionals to only listen for data changes rather than things like table height changes, etc..

The other thing I did was set a "isDtInitalized" parameter within the component class . Combined with an *ngIf on the component markup, that was also helpful.

Other things --- making sure you are running the .next() call within setTimeout()... establishing a valid subscription and then clearing it, etc..

Before destroying, for some reason, i had to also run dtInstance.clear() before dtInstance.destroy() to actually remove the old data... otherwise, sorts on a changed table would sort the initial data.

All of these added up to it working just great!

riyas145 commented 3 years ago

there is any solution for render two tables in a single view ? i want to render each tables sepereatly @shanmukhateja @l-lin @epiphanizer @shohag000 @mritunjaybs1510