Как загрузить подключаемый модуль DataTable jQuery, когда модальное окно Bootstrap полностью загружено без проблем с асинхронностью? - PullRequest
1 голос
/ 17 июня 2020

В настоящее время я использую Bootstrap v4.4.1 и nodejs bootstrap версии плагина:

require('datatables.net-bs4')(window, $);
require('datatables.net-colreorder-bs4')(window, $);
require('datatables.net-fixedheader-bs4')(window, $);

Сначала я загружаю все строки в таблице, которая находится внутри html модального окна Bootstrap. Все эти операции синхронны, поэтому я не вижу здесь никаких проблем. Каждая из функций get_* просто добавляет элементы узла с помощью jQuery.

for (var i = 0; i < cols.length; i++) {
    var col_name = cols[i];
    var name = self.get_col_name(col_name);
    var data_type = self.get_data_type(col_name)
    var attrs = self.pj_cols[col_name]['attrs'].join(', ');  // TODO: translate to icons or extract just some of them?
    var cb_export = self.get_cb_export(i, col_name);
    var sel_cur_prec = self.get_cur_prec(col_name);
    var txt_cur_unit = self.get_txt_cur_unit(col_name);
    var set_bt = self.get_set_bt(col_name, i);

    var tr = $('<tr>');
    tr.append(
        $('<td>', {html: cb_export }),
        name,
        $('<td>', {html: data_type }),
        $('<td>', {text: attrs }),
        $('<td>', {html: sel_cur_prec }),
        $('<td>', {html: txt_cur_unit }),
        $('<td>', {html: set_bt })
    );
    $('#table_column_project tbody').append(tr);
}

Затем я загружаю модальное окно программно, нажимая кнопку для запуска и показываю его

$('#modal_column_project').click();

И, наконец, я запускаю функцию DataTable(), чтобы преобразовать таблицу в DataTable

$('#column_project_win').on('shown.bs.modal', function (e) {
    $('#table_column_project').DataTable( {
        scrollY: 400,
        scrollCollapse: true,
        paging: false,
        searching: true,
        ordering: true,
        order: [[ 1, 'asc' ]],
        info: false,
        columnDefs: [
            { targets: '_all', visible: true, },
            { targets: [6], orderable: false, searchable: false, },
            { targets: [0, 2, 4, 5, 6], type: 'html'}
        ],
        initComplete: function () {
            // initially the div which is a containter has opacity 0
            $('#div_column_project').animate({ opacity: 1, }, { duration: 100, });
        },
    });
});

Иногда это работает хорошо. Часто строки никогда не появляются, а в таблице данных отображается сообщение:

No matching records found

Есть ли способ сделать это лучше, чтобы избежать этой асинхронной проблемы? Я говорю «асинхронный», потому что явно случайный.

Я также пытался добавить этот прием без хороших результатов. Я думаю, что проблема в вызове DataTable, где удаляются строки таблицы

var _check_loaded_rows = setInterval(function() {
    if ($('#table_column_project tbody tr').length == cols.length) {  // check if all rows are correctly loaded
        clearInterval(_check_loaded_rows);
        $('#table_column_project').DataTable( {  // TODO: show only when rendered
            scrollY: 400,
            scrollCollapse: true,
            paging: false,
            searching: true,
            ordering: true,
            order: [[ 1, 'asc' ]],  // this is the value by default
            info: false,
            columnDefs: [
                { targets: '_all', visible: true, },
                { targets: [6], orderable: false, searchable: false, },
                { targets: [0, 2, 4, 5, 6], type: 'html'}
            ],
            initComplete: function () {
                $('#div_column_project').animate({ opacity: 1, }, { duration: 100, });
            },
        });
    }
}, 100);

На самом деле, теперь у меня есть setTimeout с задержкой 500 мс, чтобы он работал нормально.

I также прочитал этот ответ , где плакат рекомендует использовать версию bootstrap, таблица должна быть инициализирована (я использую opacity, чтобы скрыть ее вместо display: none;) и показать DataTable Я использую встроенный метод initComplete для запуска инструкции по установке непрозрачности.

1 Ответ

1 голос
/ 19 июня 2020

Если у вас всего несколько сотен строк, почему бы не вставить их с помощью API в initComplete? Вы можете передать API другой функции (например, той, которая использует get_* методы, которые вы уже используете для заполнения (неясно, функция это или нет)):

function populate(api) {
  for (var i = 0; i < cols.length; i++) {
    ...
    api.row.add([cb_export, name, data_type, attrs, sel_cur_prec, txt_cur_unit, set_bt])
  }
  api.draw/()
}

$('#table_column_project').DataTable({
  ...
  initComplete: function() {
    const api = this.api()
    populate(api)
  }
})

Или вы можете использовать обещание и дождитесь его внутри модального обработчика событий:

function populate() {
  return new Promise(resolve => {
    ...
    if (i+1 === cols.length) resolve()
  })
}

$('#column_project_win').on('shown.bs.modal', function (e) {
  populate().then(() => {
    $('#table_column_project').DataTable( {
      ...
    })
  })
})

Если у вас нет Promise в вашем nodejs (более старые версии), вы можете использовать, например, es6-promise и

const Promise = require('es6-promise').Promise  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...