ngTemplateRef + colvis-button (Turn columns "visible":false) - TypeError: Cannot read property 'appendChild' of null
See original GitHub issueIMPORTANT: I will try to deliver a stackblitz or minimal reproduction project later. I tried to create a stackblitz or use the stackblitz “angular-datatables-gitter” but I couldn’t get them to work with ngTemplateRef. Please don’t close this issue immediately!
🪲 bug report
I have a datatable that gets initialized within “ngAfterViewInit”. It uses “ajax” to gather data. I use the datatable “server side the angular way”. I have activated the “buttons” plugin to use “colvis” and “stateSave: true”.
In my use-case I need one column with a ngTemplateRef. I initialize “this.columns” and put the column with ngTemplateRef at the end, then pass it into “dtOptions”. The Datatable initializes fine.
Once I turn one column before my ngTemplateRef column invisible and reload the page, the datatable crashes with this error:
TypeError: Cannot read property 'appendChild' of null
at EmulatedEncapsulationDomRenderer2.appendChild (platform-browser.js:670)
at angular-datatables.directive.js:79
at Array.forEach (<anonymous>)
at S.fn.init.rowCallback (angular-datatables.directive.js:68)
at jquery.dataTables.js:6682
at Function.map (jquery.min.js:2)
at _fnCallbackFire (jquery.dataTables.js:6681)
at _fnDraw (jquery.dataTables.js:3503)
at _fnAjaxUpdateDraw (jquery.dataTables.js:4169)
at jquery.dataTables.js:4009
at callback (jquery.dataTables.js:3901)
at SafeSubscriber._next (material-template-dt.component.ts:132)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
at SafeSubscriber.next (Subscriber.js:122)
at Subscriber._next (Subscriber.js:72)
at Subscriber.next (Subscriber.js:49)
at CatchSubscriber._next (Subscriber.js:72)
at CatchSubscriber.next (Subscriber.js:49)
at MapSubscriber._next (map.js:35)
at MapSubscriber.next (Subscriber.js:49)
🔬 Minimal Reproduction
IMPORTANT: Will deliver stackblitz or project later!
- Create a datatable
- Example code:
x.component.ts
dataTableActions: Array<DataTableAction> = [
{
cmd: "edit",
label: "Bearbeiten"
},
{
cmd: "delete",
label: "Löschen"
},
];
@ViewChild('dtActions') dtActions: TemplateRef<ActionsComponent>;
.
.
.
ngAfterViewInit(): void {
this.columns.push(...[
{
title: "Name",
data: "name"
},
{
title: "Age",
data: "age"
},
{
title: "Info",
data: "info"
}
]);
if (this.dataTableActions.length > 0) {
this.columns.push({
title: "Aktionen",
data: null,
orderable: false,
searchable: false,
defaultContent: "",
ngTemplateRef: {
ref: this.dtActions,
context: {
captureEvents: this.onCaptureEvent.bind(this)
}
}
});
}
this.dtOptions = {
language: german,
dom: '<l>Bfrtip',
buttons: [
{
extend: 'colvis',
columns: ':not(.noVis)'
},
'excel',
],
columnDefs: [
{
targets: "_all",
className: "valign-middle",
},
{
targets: [0],
className: "text-right noVis",
},
],
stateSave: true,
serverSide: true,
processing: true,
searchDelay: 600,
ajax: (dataTablesParameters: any, callback) => {
this.service
.getJsonLd().subscribe(resp => {
callback({
recordsTotal: resp['hydra:totalItems'],
recordsFiltered: resp['hydra:totalItems'],
data: resp['hydra:member']
});
});
},
columns: this.columns
};
onCaptureEvent(event: DataTableActionsEvent): void {
// Stuff
}
x.component.html
<ng-template #dtActions let-data="adtData" let-emitter="captureEvents">
<app-actions [actions]="dataTableActions" [data]="data" (emitter)="emitter($event)"></app-actions>
</ng-template>
<table id="material--template-dt" *ngIf="columns.length" datatable [dtOptions]="dtOptions" class="table table-striped w-100"></table>
- Load the page
- Use “colvis”-Button to hide one column before our ngTemplateRef column
- Reload the page
🎱 Expected behavior
The Datatable should render fine.
🌐 Your Environment
- node version: 16.4
- angular version: 11.2.8
- angular-cli version: 11.2.7
- jquery version: 3.6.0
- datatables version: ^1.10.20
- angular-datatables version: 12.0.0
📝 Additional context
It seems like in the line 66-80 within angular-datatables.directive.js are the issue:
// Filter columns using `ngTemplateRef`
var colsWithTemplate = columns_1.filter(function (x) { return x.ngTemplateRef && !x.ngPipeInstance; });
colsWithTemplate.forEach(function (el) {
var _a = el.ngTemplateRef, ref = _a.ref, context = _a.context;
// get <td> element which holds data using index
var index = columns_1.findIndex(function (e) { return e.data == el.data; });
var cellFromIndex = row.childNodes.item(index);
// render onto DOM
// finalize context to be sent to user
var _context = Object.assign({}, context, context === null || context === void 0 ? void 0 : context.userData, {
adtData: data
});
var instance = self.vcr.createEmbeddedView(ref, _context);
self.renderer.appendChild(cellFromIndex, instance.rootNodes[0]);
});
I think after disabling some columns and reloading the page, there is a bug with referenced indeces. Naively spoken: The column of ngTemplateRef gets a certain index beforehand (index = 3), a column gets "visible: false, the column.length only goes to index 2 and then it tries to map everything to index 3 causing a null reference error.
Issue Analytics
- State:
- Created 2 years ago
- Comments:20
Top GitHub Comments
Hi @mtrzensky
bug confirmed. Let me see if I can work something out this weekend 😃
@mtrzensky yes and yes - an updated repo project as well as new issue would help.