This one is tricky, I'll try to explain myself as best as I can.
Brief explanation:
I have DataTables library integrated in my Angular project, had it since I purchased the theme of the project several months ago. The Theme has been updated, and so I proceeded with the update of my project with latest versions.
The strange thing is that what's not working is DataTables, and DataTables have not been changed!
Code breakdown:
From X component I'm triggering a method of my shared service IBOsService.
When this method is triggered, my DatatableComponent imports the datatables library in a Promise.
Never had issues with this, until now.
In DatatableComponent:
this.tablesService.initTableData$.subscribe(() => {
if (!this.datatableInitialized) {
log.info('Starting script import promise');
Promise.all([
System.import('script-loader!my-plugins/datatables-bundle/datatables.min.js')
]).then(values => {
log.data('success', JSON.stringify(values));
this.render();
}, reason => {
log.error('error', JSON.stringify(reason));
});
}
}
);
In my console I see: DataTables success [{}]. Therefore, I understand that promise was success.
So then we enter this.render(); method.
The method goes on until this line here:
const _dataTable = element.DataTable(options);
The thing is that in my IDE, I can navigate to all variables, methods, etc... But I cannot navigate to DataTable(), therefore I guess is just not recognizing this method and that's why it throws error.
But, as the script is loaded in the promise, is normal that the IDE doesn't have the mapping of DataTables methods...
Full Component Code:
import { Component, Input, ElementRef, AfterContentInit, OnInit, Injectable, OnDestroy } from '@angular/core';
import { IBOsService } from '../../../+ibos/ibos.service';
import { Subscription } from 'rxjs/Subscription';
import { Log, Level } from 'ng2-logger';
import { logConfig } from '../../../../environments/log_config';
const log = Log.create('DataTables');
log.color = logConfig.dataTable;
declare let $: any;
@Component({
selector: 'sa-datatable',
template: `
<table class="dataTable {{tableClass}}" width="{{width}}">
<ng-content></ng-content>
</table>
`,
styles: [
require('my-plugins/datatables-bundle/datatables.min.css')
]
})
@Injectable()
export class DatatableComponent implements OnInit, OnDestroy {
@Input() public options: any;
@Input() public filter: any;
@Input() public detailsFormat: any;
@Input() public paginationLength: boolean;
@Input() public columnsHide: boolean;
@Input() public tableClass: string;
@Input() public width = '100%';
public datatableInitialized: boolean;
public subscription: Subscription;
constructor(private el: ElementRef, private tablesService: IBOsService) {
this.tablesService.refreshTable$.subscribe((tableParams) => {
this.filterData(tableParams);
}
);
this.tablesService.initTableData$.subscribe(() => {
if (!this.datatableInitialized) {
log.info('Starting script import promise');
Promise.all([
System.import('script-loader!my-plugins/datatables-bundle/datatables.min.js')
]).then(values => {
log.data('success', JSON.stringify(values));
this.render();
}, reason => {
log.error('error', JSON.stringify(reason));
});
}
}
);
}
ngOnInit() {
}
render() {
log.info('Starting render!');
const element = $(this.el.nativeElement.children[0]);
let options = this.options || {};
log.info('1 render!');
let toolbar = '';
if (options.buttons) {
toolbar += 'B';
}
log.info('2 render!');
if (this.paginationLength) {
toolbar += 'l';
}
if (this.columnsHide) {
toolbar += 'C';
}
log.info('3 render!');
if (typeof options.ajax === 'string') {
const url = options.ajax;
options.ajax = {
url: url,
// complete: function (xhr) {
//
// }
};
}
log.info('4 render!');
options = $.extend(options, {
'dom': '<\'dt-toolbar\'<\'col-xs-12 col-sm-6\'f><\'col-sm-6 col-xs-12 hidden-xs text-right\'' + toolbar + '>r>' +
't' +
'<\'dt-toolbar-footer\'<\'col-sm-6 col-xs-12 hidden-xs\'i><\'col-xs-12 col-sm-6\'p>>',
oLanguage: {
'sSearch': `<span class='input-group-addon'><i class='glyphicon glyphicon-search'></i></span>`,
'sLengthMenu': '_MENU_'
},
'autoWidth': false,
retrieve: true,
responsive: true,
initComplete: (settings, json) => {
element.parent()
.find('.input-sm')
.removeClass('input-sm')
.addClass('input-md');
}
});
log.info('5 render! element', JSON.stringify(element));
log.info('5.1 render! options', JSON.stringify(options));
const _dataTable = element.DataTable(options);
log.info('5.2 render! _dataTable', JSON.stringify(_dataTable));
if (this.filter) {
// Apply the filter
element.on('keyup change', 'thead th input[type=text]', function () {
console.log('searching?');
_dataTable
.column($(this).parent().index() + ':visible')
.search(this.value)
.draw();
});
}
log.info('6 render!');
if (!toolbar) {
element.parent().find('.dt-toolbar')
.append(
'<div class="text-right">' +
'<img src="assets/img/logo.png" alt="SmartAdmin" style="width: 111px; margin-top: 3px; margin-right: 10px;">' +
'</div>'
);
}
log.info('7 render!');
if (this.detailsFormat) {
const format = this.detailsFormat;
element.on('click', 'td.details-control', function () {
const tr = $(this).closest('tr');
const row = _dataTable.row(tr);
if (row.child.isShown()) {
row.child.hide();
tr.removeClass('shown');
} else {
row.child(format(row.data())).show();
tr.addClass('shown');
}
});
}
log.info('8 render!');
this.datatableInitialized = true;
}
filterData(tableParams) {
console.log('reloading DT... With these parameters: ' + JSON.stringify(tableParams));
const element = $(this.el.nativeElement.children[0]);
const table = element.find('table.dataTable');
log.data('current table element is: ', JSON.stringify(table));
Object.keys(tableParams).forEach(function (key) {
log.warn('current key: ', JSON.stringify(key));
table.DataTable().column(`${key}:name`).visible(tableParams[key]);
});
table.DataTable().ajax.reload();
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
You'll see I've invaded the component with logs, it helped me understand where my Component was failing.
Full Console Log:
Interesting info:
If I call the filterData(tableParams) method... The DataTable renders with no problems.
This tells me 2 things:
DataTable()is not the issue.- The issue is only on first time rendering, then, on updating, I encounter no problems.
I'm terribly sorry for my Wall of text... But I did some Sherlock Holmes by my own and couldn't find the solution.
If you need clarification or more details let me know.
Thanks in advance!
Update:
After debugging for days, I have found a possible source of error:
The thing is that s is null.
Maybe someone more experienced on DataTables or that encountered this issue could give me a hint on what's going on.
I'll be here debugging... ;)
Update 2:
After more debugging I found out that DataTables is not making the first Ajax call.
I'm using server side DataTables, with an Ajax call in POST.
Here is the code of my options object:
this.options = {
dom: 'Bfrtip',
processing: true,
serverSide: true,
pageLength: 20,
searchDelay: 1200,
ajax: {
url: this.jsonApiService.buildURL('/test_getUsers.php', 'remote'),
type: 'POST',
data: function (d) {
Object.assign(d, IBOsTable.params);
log.data('DT options obj. New params are: ', JSON.stringify(IBOsTable.params));
return d;
}
},
columns: this.initColumns,
};
This is not making the Ajax call on initialization but it's making it on table.DataTable().ajax.reload();.
This tells me that the code is not incorrect or broken (on reload() works like a charm)...
I'm still not undesrtanding fully why the initialization of this DataTables is not working... But I believe that I'm close enough!



sis not determinant. I found out that my DT is not making the first Ajax call and, therefore, doesn't have actual data to render and that's why the error occurs. The issue here is find out why is not making the first Ajax call. Cheers! ;)