javascript - Using ajax in a callback where the parent process itself relies on an ajax call
问题描述
I'm working with an application that uses DataTables to generate a HTML table which is populated by data from an ajax request.
This is fairly simple:
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;
}
},
// ...
});
There is a callback which DataTables provides, called rowCallback
(https://datatables.net/reference/option/rowCallback) which allows post processing of table rows after the table has been drawn. The key thing here is that it's after the ajax request to /get-substances.json
; the table must be populated with data because this callback is used to manipulate data within it at that point.
Within rowCallback
I'm providing an array of row ID's in my table - that is to say ID's which correspond to <tr>
elements inside #substancesTable
- and I go on to expand these rows. I can do this manually by hardcoding in an array of row ID's, e.g.
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');
}
});
The array I've hardcoded means rows 4 and 7 are expanded after the table has been populated, which is equivalent to the user clicking on them.
The problem I have is that I don't want to hardcode the array. The application stores the equivalent of var index
in Redis (cache) meaning that we can grab the data easily even if the user leaves the page. So I have added a second ajax request (outside the var substancesTable...
block) to obtain the Redis data. This makes an ajax request to populate an array, activeRows
:
var activeRows = [];
$.ajax({
url: '/view-substance/get-active-rows',
method: 'post',
cache: false,
}).done(function(data) {
activeRows = data;
console.log(activeRows);
});
I understand that the nature of ajax means my code is asynchronous. In some cases the ajax request shown above will complete before the DataTable is drawn, so I get the console.log(activeRows) appearing before the table is rendered, and in other cases it happens afterwards.
What is the correct way to make this second ajax request such that the values from it can be used in place of the hardcoded array? I appreciate I will need to convert the response to an array (since it's still JSON in the console.log
statement). But my question is focused on where to put this code such that it can be used reliably inside rowCallback
?
I have read How do I return the response from an asynchronous call? and understand about the async nature. I can't work out how to structure this to be used in a callback that's already part of an ajax request.
The application uses DataTables version 1.10.16 and jquery 3.2.1
解决方案
You can actually use the ajax
option to:
Make the first AJAX request which retrieves the active rows.
Once the active rows are retrieved, make the second AJAX request which retrieves the table data.
Example: (see full code and demo here)
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
In the above example, I separated the AJAX calls into two different functions mainly to avoid long indentation in the ajax
option when you call the $('#example').dataTable()
. The code would otherwise look like:
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){
...
}
});
I used the .always()
so that the table data would still be retrieved in case of failures in retrieving the active rows.
推荐阅读
- android - Is there a Jetpack Compose equivalent of A password field with the password visible to the user?
- magento2 - Magento 2, warranty and returns verbiage
- python - Python: ModuleNotFoundError despite module being installed
- python - Converting .txt file to CSV
- python - 与 Python 的 Hashlib 或 Crypto++ 相比,RustsCrypto 的性能不佳
- java - 如何在 Java(Linux 和 Windows)中使用操作系统的默认文件选择器
- dart - 用 File 的具体实现扩展一个类
- docker - 如何将 docker 镜像推送到 moto 服务器中的 ecr repo
- reactjs - TypeError:无法读取未定义的属性“clickNode”
- vue.js - 如何在组件 vue 中抛出脚本之前完整状态