Использование ajax в обратном вызове, когда сам родительский процесс опирается на вызов ajax - PullRequest
0 голосов
/ 08 ноября 2018

Я работаю с приложением, которое использует DataTables для создания таблицы HTML, которая заполняется данными из запроса ajax.

Это довольно просто:

var substancesTable = $('#substancesTable').DataTable({
    "processing": true,
    "serverSide": true,
    "searching": false,
    "ajax": {
        "url": "/get-substances.json",
        "method": "POST",
        "cache": false,
        "dataSrc": function (json) {

            // Update non-Datatables UI elements and perform other functions based on the ajax response
            $('#numSubstances').html(json.recordsTotal);
            drawOptionsButton(json.isFiltering);

            // Must return data for DataTables to work
            return json.data;
        }
    },
    // ... 
});

Существует обратный вызов, который предоставляет DataTables, который называется rowCallback (https://datatables.net/reference/option/rowCallback), который позволяет постобработать строки таблицы после таблицы нарисованной. Ключевым моментом здесь является то, что она после ajax-запрос к /get-substances.json; таблица должна быть заполнена данными, потому что этот обратный вызов используется для манипулирования данными внутри нее в этот момент.

В пределах rowCallback я предоставляю массив идентификаторов строк в моей таблице - то есть идентификаторы, которые соответствуют <tr> элементам внутри #substancesTable - и я продолжаю расширять эти строки. Я могу сделать это вручную путем жесткого кодирования в массиве идентификаторов строк, например,

 var substancesTable = $('#substancesTable').DataTable({
     // ...
     "rowCallback": function(row) {
       var id = $(row).find('td:first').text();
       var index = $.inArray(id, ['4', '7']); // hardcoded array

       if (index !== -1) {
           var tr = $(row).closest('tr');
           var row = substancesTable.row( tr );
           row.child.show();
           tr.addClass('active');
       }
 });

Массив, который я жестко закодировал, означает, что строки 4 и 7 расширяются после заполнения таблицы, что эквивалентно нажатию пользователем на них.

Проблема в том, что я не хочу жестко кодировать массив. Приложение хранит эквивалент var index в Redis (кеш), что означает, что мы можем легко получить данные, даже если пользователь покидает страницу. Поэтому я добавил второй запрос ajax (за пределами блока var substancesTable...) для получения данных Redis. Это делает ajax-запрос для заполнения массива, activeRows:

var activeRows = [];
$.ajax({
    url: '/view-substance/get-active-rows',
    method: 'post',
    cache: false,
}).done(function(data) {
  activeRows = data;
  console.log(activeRows);
});

Я понимаю, что природа ajax означает, что мой код асинхронный. В некоторых случаях запрос ajax, показанный выше, завершится до отрисовки DataTable, поэтому я получаю console.log (activeRows), появляющийся до визуализации таблицы, а в других случаях это происходит позже.

Как правильно сделать второй запрос ajax таким, чтобы его значения могли использоваться вместо жестко закодированного массива? Я ценю, что мне нужно будет преобразовать ответ в массив (так как это все еще JSON в операторе console.log). Но мой вопрос сосредоточен на том, где разместить этот код так, чтобы он мог надежно использоваться внутри rowCallback?

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

Любой совет будет принят.

Приложение использует DataTables версии 1.10.16 и jquery 3.2.1

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Вы можете использовать опцию ajax для:

  1. Сделайте первый AJAX-запрос, который извлекает активные строки.

  2. После извлечения активных строк выполните второй запрос AJAX, который извлекает данные таблицы.

Пример: (см. Полный код и демонстрацию здесь )

var activeRows = [];

function getActiveRows() {
  return $.ajax({
    url: '/view-substance/get-active-rows',
    type: 'POST',
    dataType: 'json'
    ...
  }).done(function(data){
    activeRows = data;
    console.log(activeRows);
  });
}

function getTableData(data, callback) {
  return $.ajax({
    url: '/get-substances.json',
    type: 'POST',
    dataType: 'json',
    'data': data // must send the `data`, but can be extended using $.extend()
    ...
  }).done(callback); // and call callback() once we've retrieved the table data
}

$('#example').dataTable({
  ajax: function(data, callback){
    getActiveRows().always(function(){
      getTableData(data, callback);
    });
  },
  rowCallback: function(row, data){
    ...
  }
});

UPDATE

В приведенном выше примере я разделил вызовы AJAX на две разные функции, главным образом, чтобы избежать длительного отступа в опции ajax при вызове $('#example').dataTable(). В противном случае код будет выглядеть так:

var activeRows = [];

$('#example').dataTable({
  ajax: function(data, callback){
    // 1. Retrieve the active rows.
    $.ajax({
      url: '/view-substance/get-active-rows',
      type: 'POST',
      dataType: 'json'
      ...
    }).done(function(res){
      activeRows = res;
      console.log(activeRows);
    }).always(function(){
      // 2. Retrieve the table data.
      $.ajax({
        url: '/get-substances.json',
        type: 'POST',
        dataType: 'json',
        'data': data // must send the `data`, but can be extended using $.extend()
        ...
      }).done(callback); // and call callback() once we've retrieved the table data
    });
  },
  rowCallback: function(row, data){
    ...
  }
});

Я использовал .always(), чтобы данные таблицы по-прежнему извлекались в случае сбоев при получении активных строк.

0 голосов
/ 12 ноября 2018

Вы можете решить свою проблему с Обещаниями. Обещания - это объекты, которые помогают вам управлять и координировать асинхронные задачи. Ваш случай будет выглядеть примерно так:

var activeRows = [];
var substancesTable = $('#substancesTable').DataTable({
     // ...
});

var pDataTable = new Promise(function(resolve, reject){
    // you wan't to resolve just once, 
    // after the table has finished processing the received data.
    // (You may want to change draw to an event that's more suitable)
    substancesTable.one('draw', resolve);
});

var pOpenRows = new Promise(function( resolve, reject ){

    $.ajax({
        url: '/view-substance/get-active-rows',
        method: 'post',
        cache: false,
    }).done(function(data) {
        // you can either save your rows globaly or give them to the resolve function
        // you don't have to do both
        activeRows = data; 
        resolve( data );
    });

});

// Here we basically create a third promise, which resolves (or rejects)
// automatically based on the promises in the array.
Promise.all([pDataTable, pOpenRows])
.then(function( values ){
    // expand your table rows here
    // Note: if you gave your rows to the resolve function you can access 
    // them here in values[1] (pDataTable's data would be in values[0])
});

Если вы хотите узнать больше об обещаниях:

Подробнее о поддержке браузера вы можете посмотреть здесь:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...