8.2 KiB
8.2 KiB
DataTable Components
Переиспользуемые компоненты для отображения интерактивных таблиц с AJAX-загрузкой, сортировкой и поиском.
Структура компонентов
public/
├── js/
│ └── modules/
│ └── DataTable.js # JS-модуль для инициализации таблиц
└── css/
└── components/
└── data-table.css # Стили для интерактивных таблиц
app/Views/components/table/
├── table.twig # Основной компонент таблицы
├── table_header.twig # Переиспользуемый заголовок
└── pagination.twig # Компонент пагинации
Быстрый старт
1. Подключение стилей и скриптов
В вашем шаблоне добавьте:
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="/css/components/data-table.css">
{% endblock %}
{% block scripts %}
{{ parent() }}
<script src="/js/modules/DataTable.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
new DataTable('your-table-id', {
url: '/your-module/table',
perPage: 10
});
});
</script>
{% endblock %}
2. Использование компонента таблицы
{{ include('@components/table/table.twig', {
id: 'products-table',
url: '/products/table',
perPage: 25,
sort: sort|default(''),
order: order|default('asc'),
filters: filters|default({}),
columns: [
{ name: 'name', label: 'Название', width: '35%' },
{ name: 'sku', label: 'Артикул', width: '15%' },
{ name: 'price', label: 'Цена', width: '15%' },
{ name: 'stock', label: 'Остаток', width: '15%' }
],
actions: { label: 'Действия', width: '20%' },
emptyMessage: 'Товары не найдены'
}) }}
Конфигурация столбцов
Каждый столбец поддерживает следующие параметры:
| Параметр | Тип | Описание |
|---|---|---|
name |
string | Идентификатор столбца (используется для сортировки и фильтрации) |
label |
string | Отображаемое название столбца |
width |
string | Ширина столбца (например, '35%', '200px') |
placeholder |
string | Текст-подсказка в поле поиска |
searchTitle |
string | Title для иконки поиска |
align |
string | CSS-класс выравнивания |
Конфигурация пагинации
Компонент автоматически получает данные из объекта pager:
// В контроллере
$pagination = [
'currentPage' => $pager->getCurrentPage(),
'totalPages' => $pager->getPageCount(),
'totalRecords' => $pager->getTotal(),
'perPage' => $perPage,
'from' => (($pager->getCurrentPage() - 1) * $perPage + 1),
'to' => min($pager->getCurrentPage() * $perPage, $pager->getTotal())
];
Пример контроллера
public function table()
{
$page = (int) ($this->request->getGet('page') ?? 1);
$perPage = (int) ($this->request->getGet('perPage') ?? 10);
$sort = $this->request->getGet('sort') ?? '';
$order = $this->request->getGet('order') ?? 'asc';
// Фильтры
$filters = [
'name' => $this->request->getGet('filters[name]') ?? '',
];
// Построение запроса
$builder = $this->model->builder();
// Применяем фильтры
if (!empty($filters['name'])) {
$builder->like('name', $filters['name']);
}
// Сортировка
if (!empty($sort)) {
$builder->orderBy($sort, $order);
}
// Пагинация
$items = $builder->paginate($perPage, 'default', $page);
$data = [
'items' => $items,
'pager' => $this->model->pager,
'perPage' => $perPage,
'sort' => $sort,
'order' => $order,
'filters' => $filters,
];
return $this->renderTwig('path/to/your/_table', $data);
}
AJAX-ответ
Для AJAX-запросов контроллер должен возвращать только tbody и tfoot:
{# _table.twig для модуля #}
{% set isAjax = app.request.headers.get('X-Requested-With') == 'XMLHttpRequest' %}
{% if isAjax %}
{# AJAX: только tbody #}
<tbody>
{% for item in items %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.price }}</td>
<td class="text-end">
<a href="...">Редактировать</a>
</td>
</tr>
{% endfor %}
</tbody>
{% if items is not empty and pager %}
<tfoot>
<tr>
<td colspan="3">
{{ include('@components/table/pagination.twig', {
pagination: paginationData,
id: 'your-table-id'
}) }}
</td>
</tr>
</tfoot>
{% endif %}
{% else %}
{# Обычный запрос: полная таблица #}
<div class="table-responsive">
{{ include('@components/table/table.twig', {
id: 'your-table-id',
url: '/your-module/table',
perPage: perPage,
columns: columns,
pagination: paginationData,
actions: { label: 'Действия' },
emptyMessage: 'Нет данных'
}) }}
</div>
{% endif %}
API DataTable
Опции при инициализации
new DataTable('container-id', {
url: '/api/endpoint', // URL для AJAX-загрузки
perPage: 10, // Записей на странице по умолчанию
debounceTime: 300, // Задержка поиска в мс
preserveSearchOnSort: true // Сохранять видимость поиска при сортировке
});
Методы
const table = new DataTable('my-table', options);
// Установка фильтра
table.setFilter('columnName', 'value');
// Установка количества записей
table.setPerPage(25);
// Переход на страницу
table.goToPage(3);
Доступные CSS-классы
| Класс | Описание |
|---|---|
.data-table |
Основной контейнер таблицы |
.header-content |
Контейнер для элементов заголовка |
.header-text |
Текст заголовка столбца |
.search-trigger |
Иконка поиска |
.sort-icon |
Иконка сортировки |
.header-search-input |
Поле ввода поиска |
.sort-icon.active |
Активная сортировка |
.pagination-wrapper |
Обёртка пагинации |
Расширение функциональности
Добавление кастомных действий
Для добавления кнопок действий в строки:
{% for client in clients %}
<tr>
<td>{{ client.name }}</td>
<td>{{ client.email }}</td>
<td class="actions-cell text-end">
<a href="{{ editUrl }}" class="btn btn-sm btn-outline-primary">
<i class="fa-solid fa-pen"></i>
</a>
</td>
</tr>
{% endfor %}
Кастомные строки
Компонент поддерживает произвольное содержимое ячеек через параметр rows:
{% set rows = [] %}
{% for product in products %}
{% set rows = rows|merge([{
cells: [
{ content: '<strong>' ~ product.name ~ '</strong>', class: '' },
{ content: product.price ~ ' ₽', class: 'text-end' }
],
actions: '<a href="...">Редактировать</a>'
}]) %}
{% endfor %}
{{ include('@components/table/table.twig', {
id: 'products-table',
rows: rows,
columns: columns,
...
}) }}