Meteor with DataTables: Meteor._atFlush TypeError

2019-01-20 01:50发布

I'm trying to use DataTables (via mrt add datatables) with Meteor. I've variously tried adding the $('#mytableid').dataTable() to the Meteor.subscribe callback, Meteor.autorun, Meteor.startup, and Template.mytemplate.rendered -- all resulting in the following exception and a No data available in table message.

Any pointers?

    Exception from Meteor._atFlush: TypeError: Cannot call method 'insertBefore' of null
        at http://localhost:3000/packages/liverange/liverange.js?bc1d62454d1fefbec95201344b27a7a5a7356293:405:27
        at LiveRange.operate (http://localhost:3000/packages/liverange/liverange.js?bc1d62454d1fefbec95201344b27a7a5a7356293:459:11)
        at LiveRange.replaceContents (http://localhost:3000/packages/liverange/liverange.js?bc1d62454d1fefbec95201344b27a7a5a7356293:403:17)
        at http://localhost:3000/packages/spark/spark.js?c202b31550c71828e583606c7a5e233ae9ca50e9:996:37
        at withEventGuard (http://localhost:3000/packages/spark/spark.js?c202b31550c71828e583606c7a5e233ae9ca50e9:105:16)
        at http://localhost:3000/packages/spark/spark.js?c202b31550c71828e583606c7a5e233ae9ca50e9:981:9
        at http://localhost:3000/packages/deps/deps-utils.js?f3fceedcb1921afe2b17e4dbd9d4c007f409eebb:106:13
        at http://localhost:3000/packages/deps/deps.js?1df0a05d3ec8fd21f591cfc485e7b03d2e2b6a01:71:15
        at Array.forEach (native)
        at Function._.each._.forEach (http://localhost:3000/packages/underscore/underscore.js?47479149fe12fc56685a9de90c5a9903390cb451:79:11)

Update: Potentially related to this issue, for which the best solution found was to call dataTable() for each row -- not ideal in that case, and potentially catastrophic in mine given the very large number of rows.

4条回答
啃猪蹄的小仙女
2楼-- · 2019-01-20 02:02

Here's my implementation of dataTables in Meteor, using CoffeeScript and HTML. It works, and it's simpler, but may be less efficient than the existing answers for large tables (it's quick in my case though).

Template Function

Template.dashboard.rendered = ->

  # Tear down and rebuild datatable
  $('table:first').dataTable
    "sPaginationType": "full_numbers"
    "bDestroy": true

HTML

<template name="dashboard">
  <table class="table table-bordered" id="datatable_3" style="min-width:1020px">
    <thead>
      <!-- code removed to save space -->
    </thead>
    <tbody id="previous-jobs-list">
      {{#each jobs}}
        {{> jobRow}}
      {{/each}}
    </tbody>
  </table>
</template>

<template name="jobRow">
  <tr>
    <td><a href="/jobs/{{number}}">{{number}}</a></td>
    <td>{{normalTime date}}</td>
    <!-- more code removed to save space -->
  </tr>
</template>

Where dashboard.jobs is a simple .find(...) on the collection.

查看更多
欢心
3楼-- · 2019-01-20 02:09

Dror's answer is the right start for sure. Here's the best practice the way I see it currently:

HTML

<template name="data_table">
    {{#constant}}
        <table class="table table-striped" id="tblData">
        </table>
    {{/constant}}
</template>

JS:

Template.data_table.created = function() {
    var _watch = Collection.find({});
    var handle = _watch.observe({
        addedAt: function (doc, atIndex, beforeId) {
            $('#tblData').is(":visible") && $('#tblData').dataTable().fnAddData(doc);
        }
        , changedAt: function(newDoc, oldDoc, atIndex) {
            $('#tblData').is(":visible") && $('#tblData').dataTable().fnUpdate(newDoc, atIndex);
        }
        , removedAt: function(oldDoc, atIndex) {
            $('#tblData').is(":visible") && $('#tblData').dataTable().fnRemove(atIndex);
        }
    });
};

Template.data_table.rendered = function () {

    if (!($("#tblData").hasClass("dataTable"))) {
        $('#tblStudents').dataTable({
            "aaSorting": []
            , "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>"
            , "sPaginationType": "bootstrap"
            , "oLanguage": {
                "sLengthMenu": "_MENU_ records per page"
            }
            , "aoColumns": [
                { "sTitle": "First Data", "mData": function (data) { return data.firstData },
            ]
        });

        $('#tblData').dataTable().fnClearTable();
        $('#tblData').dataTable().fnAddData(Collection.find().fetch());
    }
};
查看更多
叛逆
4楼-- · 2019-01-20 02:11

Since Meteor is reactive, you need to create an empty table first and then add the rows.

Create the table:

$('#myTable').dataTable( {
    "aoColumns": cols, //the column titles
    "sDom": gridDom
} );

Add rows should look something like:

 var myCursor = myCollection.find({});
 var handle = myCursor.observe({
  added: function (row) {
   //Add in here the data to the table
  },

});

查看更多
对你真心纯属浪费
5楼-- · 2019-01-20 02:12

The answered approach still stands but requires some changes after two years.

  1. There is no such thing as constant anymore.
  2. Instead of an empty table, put the thead but NOT the tbody. This way, columns are defined properly.
  3. Put the observer in the onRendered (not rendered anymore) and NOT on onCreated (not created anymore). Otherwise, table is only filled at first creation and not when you come back.
  4. clear and fill methods on the onRendered are not required.
  5. In the observe method, make sure that you are adding an array and not an object. Otherwise fnAdd cannot render content
  6. fnRemove is no more. Use fnDeleteRow instead
  7. I'm not sure if the "is visible check" is required or not. There does not seem to be a problem when you remove it too, so I removed it.

With the above remarks, here is the code:

Template.creamDealsList.onRendered(function () {
   if (!($("#tblData").hasClass("dataTable"))) {
      $('#tblStudents').dataTable({
        // Those configuration are not really important, use it as they are convenient to you
        "aaSorting": []
        , "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>"
        , "sPaginationType": "bootstrap"
        , "oLanguage": {
            "sLengthMenu": "_MENU_ records per page"
        }
        , "aoColumns": [
            { "sTitle": "First Data", "mData": function (data) { return data.firstData },
        ]
    });
   }
  var _watch = Collection.find({});
  var convertDocToArray = function (doc) {return [doc._id, doc.name]}
  var handle = _watch.observe({
    addedAt: function (doc, atIndex, beforeId) {
        $('#tblData').dataTable().fnAddData(convertDocToArray(doc));
    }
    , changedAt: function(newDoc, oldDoc, atIndex) {
        $('#tblData').dataTable().fnUpdate(convertDocToArray(newDoc), atIndex);
    }
    , removedAt: function(oldDoc, atIndex) {
        $('#tblData').dataTable().fnDeleteRow(atIndex);
    }
  });
})

And simple HTML would be like:

<template name="data_table">
    <table class="table table-striped" id="tblData">
        <thead>
            <th>Id</th>
            <th>Name</th>
        </thead>
    </table>
</template>

This worked at least for me. I'm not seeing flush errors anymore. And also, when you render later, sometimes, there was a no data available message in the datatable and that disappeared as well.

查看更多
登录 后发表回答