2017-06-16 76 views
2

这一个很棘手,我会尽我所能地尽力解释自己。Angular - Datatables:TypeError:无法读取null的属性'nodeName'

简要说明:

我有数据表库集成在我的角度项目,有它,因为我几个月前购买了该项目的主题。主题已更新,因此我用最新版本继续更新了我的项目。

奇怪的是,什么不工作是DataTables和DataTables没有改变!

代码故障:

从X组件我触发我的共享服务IBOsService的方法。

当触发此方法时,我的DatatableComponent导入Promise中的数据表库。

直到现在,从来没有这个问题。

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)); 
        }); 
       } 
      } 
     ); 

在我的控制台我看到:DataTables success [{}]。因此,我明白这个承诺就是成功。

那么我们再输入this.render();的方法。

的方法一直持续到这条线的位置:

const _dataTable = element.DataTable(options); 

的事情是,在我的IDE,我可以浏览到所有变量,方法,等等......但我不能浏览到DataTable(),因此我猜测只是不认识这种方法,这就是为什么它会抛出错误。

,因为脚本在加载的承诺,是正常的IDE不具备的数据表方法的映射......

全部组件代码:

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(); 
     } 
    } 
} 

你会看到我用日志侵入了组件,它帮助我了解组件在哪里失败。

完全控制台登录:

enter image description here

有趣的信息:

如果我称之为filterData(tableParams)方法... DataTable中呈现,没有任何问题。

这告诉我两件事情:

  1. DataTable()不是问题。
  2. 这个问题只在第一次渲染,然后,在更新时,我没有遇到任何问题。

我为我的文字的墙非常抱歉......但我做了一些福尔摩斯由我自己,无法找到解决方案。

如果你需要澄清或更多的细节让我知道。

在此先感谢!

更新:

调试天后,我发现错误的可能来源:

enter image description here

的事情是,snull

也许有人对DataTables有更多的经验或遇到这个问题可能会给我提示发生了什么。

我会在这里调试...;)

更新2:

经过调试,我发现,数据表不使第一Ajax调用

我正在使用服务器端数据表,在POST中使用Ajax调用。

这里是我的options对象的代码:

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, 
}; 

不是让Ajax调用在初始化,但它使得它table.DataTable().ajax.reload();

这告诉我,代码不是不正确或损坏(上reload()作品就像一个魅力)...

我还没有完全undesrtanding为什么这个数据表的初始化是不工作...但是,我相信我已经够接近了!

+0

如果是S定义?我没有看到该代码 – Bindrid

+0

@Bindrid嗨! 's'不是行列式。我发现我的DT没有进行第一次Ajax调用,因此没有实际的数据进行渲染,这就是错误发生的原因。这里的问题是找出为什么不做第一个Ajax调用。干杯! ;) – SrAxi

回答

0

我终于找到了错误的来源!

如果你看过我的更新你会发现我想通了,我的数据表并没有使第一Ajax调用,但reload()是完美的工作:

这是因为在初始化JavaScript错误不允许进行Ajax调用。提示:TypeError:无法读取空值的属性'nodeName'。

speaking to Allan (from DataTables)之后,他给我提示错误可能是由于thead的列数不等于tbody的列数引起的。

所以我查了一下.html,发现我有一些空的<thead><tfoot>标签。我评论他们......并且工作!

enter image description here

我已经跑了个遍每一个可能的解决方案:

  • 依赖
  • 实例化的数据表服务器端
  • 的角2应用
  • 许多不同的方式中的jQuery库在代码中改变方法:TypeScript,jQuery,Ng2等...

最后,我只需要清除我的DataTable加载到的div,以避免此问题。

有趣的事实:随着该.HTML我的应用程序工作了3/4个月,没有任何问题,用数据表没有问题到现在为止...

相关问题