Сценарий
У нас есть таблица сотрудников, которая выглядит так:
который содержит 16 530 строк сотрудников. Как и ожидалось, эта таблица загружается более 30 секунд. Мне поручили как-то его ускорить.
В качестве временной меры мы заблокировали этот пользовательский интерфейс экраном загрузки при загрузке страницы.
Что мы делали
- Получить всех сотрудников из API для магазина
- For each employee
- convert it into an
Employee
object - получить его HTML-представление (через рендеринг шаблона)
- поместите это HTML-представление в коллекцию
- convert it into an
- инициализировать объект Datatables с этой коллекцией
- настроить столбцы Datatables и нарисовать таблицу
Что я сейчас делаю
- Получить всех сотрудников из API для магазина
- For each employee
- convert it into an
Employee
object - поместите этот объект в коллекцию
- convert it into an
- инициализировать объект Datatables с этой коллекцией
- adjust the Datatables columns and draw the table
- On
rowCallback
, do the custom rendering logic
- On
Реализация
На $(document).ready
у нас есть следующая логика настройки таблицы:
var addbutton =
'<button onclick="PopModel()" class="btn btn-success float-sm-left rounded"><i class="fa fa-user-plus" aria-hidden="true"></i> Add Employee</button>'
var table_height = $('#with-content')
.height() - 175
var table = InitializeDatatable('#data-table', table_height, addbutton, 1, {
paging: true,
pageLength: 50,
deferRender: true,
/**
* Invoked when a row is being created
* @param {DomRow} row the row that datatables comes up with
* @param {Object | Array} data the data we pass to datatables for the current row
*/
createdRow: function(row, data) {
data[0] = 'some image'
},
/**
* Invoked after a row has been created
* @param {DomRow} row the row that datatables comes up with
* @param {Object | Array} data the data we pass to datatables for the current row
*/
rowCallback: function(row, data) {
// render (or replace!) row here
setTimeout(function() {
let employeeRowTR = new Employee()
.ExtractFrom(data)
.ToHTML()
$(row).html(employeeRowTR.html())
}, 4)
}
})
let start = Date.now()
GetEmployees(function (result, success) {
if (success) {
let tableRows = []
var ran = 0;
for (let i = 0; i < result.length; i++) {
const element = result[i];
// progress bar logic
setTimeout(function () {
ran++;
// adjust the progress bar state if it is defined
if ($progressBar != null) {
var percentValue = (ran / result.length) * 100
$progressBar
.css('width', percentValue + "%")
if (percentValue == 100) {
$('.dataTables_scrollBody')
.LoadingOverlay('hide')
}
}
// extract an Employee object and add its HTML representation to datatables
var employee = new Employee()
.ExtractFrom(element)
tableRows.push(employee)
if (ran == result.length) {
table.rows.add(tableRows)
table.columns.adjust()
.draw();
}
if (ran == 50) {
$('.dataTables_scrollBody')
.LoadingOverlay("show", {
image: "",
custom: $progressBarDiv
});
$progressBar = $('#progressbar .progress-bar')
}
}, 1)
}
if (result.length == 0 && $('#task-selectpicker option')
.length == 0) {
Alert("It looks like there are no tasks created, would you like to create them before creating your employees?", "This can make things easier when setting up your employees.", function () {
window.location = '/task/index'
})
}
} else {
var response = new ErrorResponse(result.responseText)
response.Show()
}
})
Наш InitializeDatatable
определяется как:
// Datatables
function InitializeDatatable(selector, table_height, addbutton, autoColumn = 0, customOptions = {}) {
var randomButtonID = RandomID()
var defaultOptions = {
dom: `<"${randomButtonID}-addbutton">frtip`,
scrollY: table_height,
scrollCollapse: true,
paging: true,
info: false,
order: [
[autoColumn, 'asc']
],
deferRender : true
}
$.extend(defaultOptions, customOptions)
var table = $(selector)
.DataTable(defaultOptions)
$(`div.${randomButtonID}-addbutton`)
.html(addbutton)
return table
}
Обратите внимание, что как только сотрудники извлекаются из базы данных, мы конвертируем их в объекты Employee
, которые имеют следующую логику представления HTML:
ToHTML() {
// load in the employee template
if (employeeTemplate == null) {
employeeTemplate = FetchTemplate('employee/employee.html', "employee-template");
}
// render it with this
var $element = employeeTemplate.tmpl(this);
// get the picture and attach it directly to the view element that's being rendered
this.GetPicture(function (picture) {
$element.find('.person-image')
.attr('src', picture.Picture)
.data(picture);
});
// attach this model data to the view element and return it.
return $element.data("model", this);
}
Элемент таблицы (получен из серверной части)
<div id="with-content" class="table-full">
<table id="data-table" class="table table-striped table-bordered" cellspacing="0" width="95%">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">ID</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Active</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
Проблема
Этот код, как он написан сейчас, ожидает, что каждый объект data
будет массивом, поэтому он жалуется на загрузку страницы:
При отклонении оповещения будут отображаться элементы строки таблицы, но когда вы нажмете на следующую страницу, оповещение появится снова. .
Указав некоторые columns
следующим образом:
columns : [
{ data : "Id" },
{ data : "FirstName" },
{ data : "LastName" },
{ data : "IsActive" }
]
создает следующую проблему:
Что я должен делать?
columns.render
, подходит ли это также для асинхронных операций, таких как получение URL-адресов изображений из API? Я пытался сделать это вcreatedRow
, но он не был готов, когда был вызванrowCallback
. 24.01.2019