# Компонент динамических таблиц DataTable ## Общее описание Компонент DataTable представляет собой универсальную систему для отображения интерактивных таблиц с поддержкой AJAX-загрузки данных, сортировки по столбцам, поиска и пагинации. Система построена на трёх уровнях: серверная часть (контроллер с методом `prepareTableData`), клиентская часть (JavaScript-модуль DataTable) и уровень представления (компоненты Twig). Архитектура компонента обеспечивает бесшовную работу как при серверном рендеринге (первичная загрузка страницы), так и при AJAX-обновлениях (фильтрация, сортировка, пагинация). При серверном рендеринге таблица отображается сразу с данными, при этом JavaScript автоматически определяет наличие данных и пропускает избыточный AJAX-запрос. При любых действиях пользователя (сортировка, фильтрация, переход по страницам) данные подгружаются через AJAX, а клиентский модуль обновляет только tbody и tfoot, сохраняя заголовок таблицы неизменным. --- ## Структура компонентов ### Файловая структура Компонент таблицы состоит из нескольких файлов, организованных по функциональному признаку. JavaScript-модуль расположен в `public/assets/js/modules/DataTable.js` и отвечает за все интерактивные взаимодействия на клиенте. Стили находятся в `public/assets/css/modules/data-table.css` и обеспечивают визуальное оформление элементов управления таблицей. Шаблоны Twig размещены в директории `app/Views/components/table/` и включают основной компонент таблицы, заголовок, пагинацию и макросы для рендеринга действий. Основные файлы компонента: - `table.twig` — универсальный компонент таблицы, включающий заголовок, тело и футер с пагинацией - `table_header.twig` — переиспользуемый заголовок с поддержкой сортировки и поиска - `pagination.twig` — компонент пагинации с навигацией по страницам - `ajax_table.twig` — упрощённый tbody для AJAX-ответов без заголовка - `macros.twig` — Twig-макросы для рендеринга кнопок действий ### Интеграция с BaseController Класс BaseController предоставляет готовую инфраструктуру для работы с таблицами через методы `getTableConfig()`, `prepareTableData()`, `renderTable()` и `table()`. Метод `getTableConfig()` возвращает конфигурацию таблицы, определяющую модель данных, колонки, правила поиска и сортировки, а также действия для каждой строки. Метод `prepareTableData()` выполняет всю логику обработки параметров запроса (пагинация, сортировка, фильтрация), формирует данные и возвращает их в структурированном виде для передачи в шаблон. Метод `renderTable()` принимает конфигурацию и возвращает HTML-код таблицы. Метод `table()` является HTTP-обработчиком для AJAX-запросов, который возвращает только tbody и tfoot без заголовка. --- ## Подключение в контроллере ### Конфигурация таблицы Каждый контроллер модуля должен определить конфигурацию таблицы через метод `getTableConfig()`. Конфигурация представляет собой ассоциативный массив с обязательными и опциональными параметрами. Обязательными параметрами являются `model` (экземпляр модели для выборки данных) и `columns` (описание колонок таблицы). Опциональные параметры позволяют настроить поведение поиска, сортировки, действий и отображения пустого состояния. ```php class Clients extends BaseController { protected ClientModel $clientModel; public function __construct() { $this->clientModel = new ClientModel(); } protected function getTableConfig(): array { return [ 'id' => 'clients-table', 'url' => '/clients/table', 'model' => $this->clientModel, 'columns' => [ 'name' => ['label' => 'Имя / Название', 'width' => '40%'], 'email' => ['label' => 'Email', 'width' => '25%'], 'phone' => ['label' => 'Телефон', 'width' => '20%'], 'created_at' => ['label' => 'Создан', 'width' => '15%'], ], 'searchable' => ['name', 'email', 'phone'], 'sortable' => ['name', 'email', 'phone', 'created_at'], 'defaultSort' => 'name', 'order' => 'asc', 'actions' => ['label' => 'Действия', 'width' => '15%'], 'actionsConfig' => [ [ 'label' => 'Редактировать', 'url' => '/clients/edit/{id}', 'icon' => 'fa-solid fa-pen', 'class' => 'btn-outline-primary', 'type' => 'edit', ], [ 'label' => 'Удалить', 'url' => '/clients/delete/{id}', 'icon' => 'fa-solid fa-trash', 'class' => 'btn-outline-danger', 'type' => 'delete', 'confirm' => 'Вы уверены, что хотите удалить этого клиента?', ], ], 'emptyMessage' => 'Клиентов пока нет', 'emptyIcon' => 'fa-solid fa-users', 'emptyActionUrl' => base_url('/clients/new'), 'emptyActionLabel' => 'Добавить клиента', 'emptyActionIcon' => 'fa-solid fa-plus', 'can_edit' => $this->access->canEdit('clients'), 'can_delete' => $this->access->canDelete('clients'), ]; } } ``` ### Параметры конфигурации Параметр `id` задаёт уникальный идентификатор контейнера таблицы и используется для инициализации JavaScript-модуля. Параметр `url` определяет endpoint для AJAX-загрузки данных. Параметр `model` указывает экземпляр модели CodeIgniter, которая используется для запроса данных. Модель автоматически фильтруется по организации через трейт `TenantScopedModel` при его наличии. Параметр `columns` описывает структуру колонок таблицы. Ключ массива соответствует имени поля в данных, а значение — ассоциативному массиву с параметрами отображения. Параметр `label` задаёт заголовок колонки, параметр `width` — ширину колонки в процентах или пикселях. Опционально можно указать `placeholder` для поля поиска, `searchTitle` для tooltip-подсказки и `align` для CSS-класса выравнивания содержимого. Параметр `searchable` определяет массив имён колонок, по которым разрешён поиск. Эти колонки получат иконку поиска в заголовке. Параметр `sortable` определяет массив имён колонок, по которым разрешена сортировка. При клике по заголовку сортируемой колонки таблица пересортируется по этому полю. Параметры `defaultSort` и `order` задают поле и направление сортировки по умолчанию. ### Методы контроллера для таблицы Основной метод для отображения страницы с таблицей выглядит следующим образом: ```php public function index() { // Проверка прав доступа if (!$this->access->canView('clients')) { return $this->forbiddenResponse('Нет прав для просмотра клиентов'); } $config = $this->getTableConfig(); return $this->renderTwig('@Clients/index', [ 'title' => 'Клиенты', 'tableHtml' => $this->renderTable($config), 'can_create' => $this->access->canCreate('clients'), 'can_edit' => $this->access->canEdit('clients'), 'can_delete' => $this->access->canDelete('clients'), ]); } ``` Метод для AJAX-загрузки данных таблицы использует встроенную логику BaseController: ```php public function table(?array $config = null, ?string $pageUrl = null) { // Проверка прав доступа if (!$this->access->canView('clients')) { return $this->forbiddenResponse('Нет прав для просмотра клиентов'); } return parent::table($config, '/clients'); } ``` Метод `table()` автоматически определяет тип запроса (обычный или AJAX) и возвращает либо полную таблицу, либо только tbody и tfoot соответственно. Для определения типа запроса используется заголовок `X-Requested-With` или параметр `format=partial`. --- ## Подключение в шаблоне ### Базовое подключение Для подключения таблицы в шаблоне Twig используется компонент `table.twig`. Компонент принимает данные из метода `renderTable()` контроллера, который возвращает полностью сформированный HTML: ```twig {# app/Modules/Clients/Views/index.twig #} {% extends 'layouts/base.twig' %} {% block title %}Клиенты{% endblock %} {% block content %}
{{ tableHtml|raw }}
{% endblock %} {% block scripts %} {{ parent() }} {% endblock %} ``` ### Прямое использование компонента При необходимости таблицу можно подключить напрямую через `include`, передав все параметры вручную: ```twig {% from '@components/table/macros.twig' import render_actions %}
{{ include('@components/table/table.twig', { id: 'my-table', url: '/my-module/table', perPage: 25, sort: sort|default(''), order: order|default('asc'), filters: filters|default({}), items: items, pagerDetails: pagerDetails, columns: { name: { label: 'Название', width: '40%' }, email: { label: 'Email', width: '30%' }, status: { label: 'Статус', width: '20%' }, }, actions: { label: 'Действия', width: '10%' }, actionsConfig: [ { label: 'Ред.', url: '/edit/{id}', icon: 'fa-solid fa-pen', class: 'btn-outline-primary' }, ], can_edit: can_edit|default(true), can_delete: can_delete|default(true), emptyMessage: 'Записей не найдено', emptyIcon: 'fa-solid fa-inbox', emptyActionUrl: url('/create'), emptyActionLabel: 'Создать', emptyActionIcon: 'fa-solid fa-plus', onRowClick: 'openClientDetails', tableClass: 'table-sm', }) }}
``` ### Поддержка render_cell и render_actions В шаблонах Twig доступны глобальные функции для рендеринга ячеек и действий. Функция `render_cell()` автоматически обрабатывает значение ячейки в зависимости от типа данных: ```twig {# Ячейка с автоматическим форматированием #} {{ render_cell(item, 'name')|raw }} {{ render_cell(item, 'email')|raw }} {{ render_cell(item, 'price')|raw }} {{ render_cell(item, 'created_at')|raw }} {# Ячейка с кастомным классом #} {{ render_cell(item, 'status', { class: 'badge bg-success' })|raw }} ``` Функция `render_actions()` рендерит кнопки действий для строки таблицы: ```twig {% set actions = [ { label: 'Ред.', url: '/edit/' ~ item.id, icon: 'fa-solid fa-pen', class: 'btn-outline-primary' }, { label: 'Удалить', url: '/delete/' ~ item.id, icon: 'fa-solid fa-trash', class: 'btn-outline-danger' }, ] %} {{ render_actions(actions)|raw }} ``` --- ## Конфигурация колонок ### Параметры колонки Каждая колонка описывается массивом с возможными параметрами. Обязательным параметром является только `label`, остальные опциональны: ```php 'columns' => [ 'name' => [ 'label' => 'Название', 'width' => '40%', 'placeholder' => 'Поиск по названию', 'searchTitle' => 'Нажмите для поиска', 'align' => 'text-start', ], 'price' => [ 'label' => 'Цена', 'width' => '15%', 'align' => 'text-end', ], 'status' => [ 'label' => 'Статус', 'width' => '15%', ], ] ``` Параметр `width` задаёт ширину колонки и может быть указан в процентах или пикселях. Рекомендуется использовать проценты для адаптивности или комбинировать фиксированные и относительные значения. Сумма ширин всех колонок обычно должна составлять 100% с учётом колонки действий. ### Поля searchable и sortable Массив `searchable` определяет поля, по которым разрешён поиск. При указании поля в этом массиве в заголовке колонки появится иконка поиска, при клике на которую отобразится поле ввода: ```php 'searchable' => ['name', 'email', 'phone', 'company'], ``` Массив `sortable` определяет поля, по которым разрешена сортировка. При клике по заголовку сортируемой колонки таблица пересортируется по этому полю, при повторном клике направление сортировки меняется на противоположное: ```php 'sortable' => ['name', 'email', 'phone', 'created_at', 'price'], ``` Важно: имена полей в `searchable` и `sortable` должны соответствовать ключам массива `columns` и именам полей в базе данных. --- ## Конфигурация действий ### Структура actionsConfig Параметр `actionsConfig` определяет кнопки действий для каждой строки таблицы. Каждое действие описывается массивом с параметрами: ```php 'actionsConfig' => [ [ 'label' => '', 'url' => '/clients/edit/{id}', 'icon' => 'fa-solid fa-pen', 'class' => 'btn-outline-primary', 'title' => 'Редактировать', 'type' => 'edit', 'confirm' => null, ], [ 'label' => '', 'url' => '/clients/delete/{id}', 'icon' => 'fa-solid fa-trash', 'class' => 'btn-outline-danger', 'title' => 'Удалить', 'type' => 'delete', 'confirm' => 'Вы уверены?', ], ], ``` Параметр `url` поддерживает подстановку `{id}` для автоматической замены на идентификатор записи. Параметр `type` используется для фильтрации действий по правам доступа: действия с `type === 'edit'` показываются только при `can_edit === true`, действия с `type === 'delete'` — только при `can_delete === true`. Параметр `confirm` добавляет подтверждение действия через стандартный `confirm()` в JavaScript. ### Кастомные действия Помимо типовых действий редактирования и удаления, можно определять кастомные действия: ```php 'actionsConfig' => [ [ 'label' => 'Просмотр', 'url' => '/clients/view/{id}', 'icon' => 'fa-solid fa-eye', 'class' => 'btn-outline-secondary', 'title' => 'Просмотр клиента', ], [ 'label' => 'Создать сделку', 'url' => '/deals/create?client_id={id}', 'icon' => 'fa-solid fa-file-contract', 'class' => 'btn-outline-success', 'title' => 'Создать сделку', ], [ 'label' => 'Записать', 'url' => '/bookings/new?client_id={id}', 'icon' => 'fa-solid fa-calendar', 'class' => 'btn-outline-primary', 'title' => 'Запись на приём', ], ], ``` --- ## Клиентская инициализация ### Базовая инициализация JavaScript-модуль DataTable инициализируется для каждой таблицы на странице. При инициализации передаются параметры конфигурации: ```javascript document.addEventListener('DOMContentLoaded', function() { new DataTable('clients-table', { url: '/clients/table', perPage: 10, debounceTime: 300, preserveSearchOnSort: true }); }); ``` Параметр `url` задаёт endpoint для AJAX-загрузки данных. Параметр `perPage` определяет количество записей на странице по умолчанию. Параметр `debounceTime` задаёт задержку в миллисекундах перед выполнением поиска (защита от частых запросов при вводе). Параметр `preserveSearchOnSort` определяет, сохранять ли видимость полей поиска при сортировке. ### Методы DataTable После инициализации экземпляр DataTable предоставляет методы для программного управления таблицей: ```javascript const table = new DataTable('my-table', options); // Установка фильтра table.setFilter('name', 'Поисковый запрос'); // Установка количества записей на странице table.setPerPage(25); // Переход на конкретную страницу table.goToPage(3); // Перезагрузка данных table.loadData(); ``` --- ## Пустое состояние и действия ### Конфигурация пустого состояния При отсутствии данных в таблице отображается пустое состояние с возможностью действия. Параметры конфигурации: ```php 'emptyMessage' => 'Клиентов пока нет', 'emptyIcon' => 'fa-solid fa-users', 'emptyActionUrl' => base_url('/clients/new'), 'emptyActionLabel' => 'Добавить клиента', 'emptyActionIcon' => 'fa-solid fa-plus', ``` Параметр `emptyMessage` задаёт текст сообщения. Параметр `emptyIcon` указывает FontAwesome-иконку для отображения над сообщением. Параметры `emptyActionUrl`, `emptyActionLabel` и `emptyActionIcon` определяют кнопку действия при пустом состоянии. ### Условное скрытие действия Кнопка действия при пустом состоянии отображается только если у пользователя есть право на создание: ```php 'emptyActionUrl' => $this->access->canCreate('clients') ? base_url('/clients/new') : null, 'emptyActionLabel' => $this->access->canCreate('clients') ? 'Добавить клиента' : null, ``` --- ## Обработка special fieldMap ### Проблема несоответствия имён полей При работе с моделями часто возникает ситуация, когда имя поля в базе данных отличается от имени свойства в Twig-шаблоне или имени параметра для фильтрации. Например, поле `client_name` в базе данных должно отображаться как «Клиент» и фильтроваться по параметру `client`. Для решения этой проблемы используется параметр `fieldMap`: ```php protected function getTableConfig(): array { return [ 'id' => 'deals-table', 'url' => '/deals/table', 'model' => $this->dealModel, 'columns' => [ 'client_name' => ['label' => 'Клиент', 'width' => '30%'], 'title' => ['label' => 'Сделка', 'width' => '25%'], 'amount' => ['label' => 'Сумма', 'width' => '15%'], 'stage' => ['label' => 'Этап', 'width' => '15%'], 'created_at' => ['label' => 'Создан', 'width' => '15%'], ], 'searchable' => ['client_name', 'title', 'stage'], 'sortable' => ['client_name', 'title', 'amount', 'stage', 'created_at'], 'defaultSort' => 'created_at', 'order' => 'desc', // fieldMap для маппинга параметров фильтрации на реальные поля 'fieldMap' => [ 'client' => 'client_name', // filters[client] -> client_name 'stage' => 'stage', ], // ... остальные параметры ]; } ``` При использовании `fieldMap` параметры фильтрации из URL (`filters[client]`) автоматически маппятся на реальное поле базы данных (`client_name`). Это позволяет использовать понятные имена параметров в URL при сохранении корректных имён полей в запросе к базе данных. --- ## Кастомные scope для запросов ### Использование callable scope Когда стандартной фильтрации недостаточно (например, нужны JOIN-ы с другими таблицами или сложные условия), можно использовать параметр `scope`. Это callable-функция, которая получает builder и полностью контролирует формирование запроса: ```php protected function getTableConfig(): array { return [ 'id' => 'deals-table', 'url' => '/deals/table', 'model' => $this->dealModel, // ... columns, searchable, sortable и т.д. // Кастомный scope для сложных запросов 'scope' => function($builder) { $builder->resetQuery(); $builder->select('d.*, c.name as client_name, c.email as client_email') ->from('deals d') ->join('clients c', 'c.id = d.client_id', 'left') ->where('d.organization_id', session()->get('active_org_id')); // Дополнительная фильтрация по статусу $status = $this->request->getGet('filters[status]'); if ($status && $status !== 'all') { $builder->where('d.status', $status); } // Фильтрация по диапазону дат $dateFrom = $this->request->getGet('filters[date_from]'); $dateTo = $this->request->getGet('filters[date_to]'); if ($dateFrom) { $builder->where('d.created_at >=', $dateFrom); } if ($dateTo) { $builder->where('d.created_at <=', $dateTo . ' 23:59:59'); } }, // fieldMap для JOIN-полей 'fieldMap' => [ 'client' => 'c.name', 'client_email' => 'c.email', ], ]; } ``` При использовании `scope` параметр `model` игнорируется для построения запроса, и `scope` полностью контролирует SELECT, FROM, JOIN и WHERE. Параметры сортировки и фильтрации всё ещё применяются к builder после выполнения scope, поэтому в `fieldMap` нужно указывать полные имена полей с алиасами таблиц. --- ## Практические примеры ### Пример 1: Таблица клиентов ```php class Clients extends BaseController { protected ClientModel $clientModel; public function __construct() { $this->clientModel = new ClientModel(); } public function index() { if (!$this->access->canView('clients')) { return $this->forbiddenResponse('Нет прав для просмотра'); } return $this->renderTwig('@Clients/index', [ 'title' => 'Клиенты', 'tableHtml' => $this->renderTable($this->getTableConfig()), 'can_create' => $this->access->canCreate('clients'), ]); } public function table() { if (!$this->access->canView('clients')) { return $this->forbiddenResponse('Нет прав'); } return parent::table($this->getTableConfig(), '/clients'); } protected function getTableConfig(): array { return [ 'id' => 'clients-table', 'url' => '/clients/table', 'model' => $this->clientModel, 'columns' => [ 'name' => ['label' => 'Имя / Название', 'width' => '35%'], 'email' => ['label' => 'Email', 'width' => '25%'], 'phone' => ['label' => 'Телефон', 'width' => '20%'], 'source' => ['label' => 'Источник', 'width' => '10%'], 'created_at' => ['label' => 'Создан', 'width' => '10%'], ], 'searchable' => ['name', 'email', 'phone'], 'sortable' => ['name', 'email', 'phone', 'source', 'created_at'], 'defaultSort' => 'created_at', 'order' => 'desc', 'actions' => ['label' => '', 'width' => '5%'], 'actionsConfig' => [ [ 'label' => '', 'url' => '/clients/edit/{id}', 'icon' => 'fa-solid fa-pen', 'class' => 'btn-outline-primary btn-sm', 'title' => 'Редактировать', 'type' => 'edit', ], [ 'label' => '', 'url' => '/clients/delete/{id}', 'icon' => 'fa-solid fa-trash', 'class' => 'btn-outline-danger btn-sm', 'title' => 'Удалить', 'type' => 'delete', 'confirm' => 'Удалить клиента?', ], ], 'emptyMessage' => 'Клиентов пока нет', 'emptyIcon' => 'fa-solid fa-users', 'emptyActionUrl' => $this->access->canCreate('clients') ? '/clients/new' : null, 'emptyActionLabel' => $this->access->canCreate('clients') ? 'Добавить клиента' : null, 'emptyActionIcon' => 'fa-solid fa-plus', 'can_edit' => $this->access->canEdit('clients'), 'can_delete' => $this->access->canDelete('clients'), ]; } } ``` ### Пример 2: Таблица с кастомным рендерингом ячеек ```php protected function getTableConfig(): array { return [ 'id' => 'deals-table', 'url' => '/deals/table', 'model' => $this->dealModel, 'columns' => [ 'client_name' => ['label' => 'Клиент', 'width' => '25%'], 'title' => ['label' => 'Сделка', 'width' => '25%'], 'amount' => ['label' => 'Сумма', 'width' => '15%'], 'stage' => ['label' => 'Этап', 'width' => '15%'], 'status' => ['label' => 'Статус', 'width' => '10%'], 'created_at' => ['label' => 'Создан', 'width' => '10%'], ], 'searchable' => ['client_name', 'title', 'stage'], 'sortable' => ['client_name', 'title', 'amount', 'stage', 'created_at'], 'defaultSort' => 'created_at', 'order' => 'desc', 'actions' => ['label' => '', 'width' => '5%'], 'actionsConfig' => [ [ 'label' => '', 'url' => '/deals/edit/{id}', 'icon' => 'fa-solid fa-pen', 'class' => 'btn-outline-primary btn-sm', 'type' => 'edit', ], ], 'can_edit' => $this->access->canEdit('deals'), ]; } ``` В шаблоне Twig можно добавить кастомный рендеринг ячеек через Twig-фильтры: ```twig {# В шаблоне ячейки с форматированием #} {{ item.title }} {% if item.description %}
{{ item.description|slice(0, 50) }}... {% endif %} {{ item.amount|number_format(0, ',', ' ') }} ₽ {% if item.status == 'active' %} Активна {% elseif item.status == 'won' %} Выиграна {% elseif item.status == 'lost' %} Проиграна {% endif %} ``` --- ## Проверка при создании модуля ### Чек-лист при добавлении новой таблицы При создании нового модуля с таблицей необходимо выполнить следующие действия: **В контроллере:** - Определить метод `getTableConfig()` с обязательными параметрами (`id`, `url`, `model`, `columns`) - Указать `searchable` и `sortable` массивы с корректными именами полей - Настроить `actionsConfig` с кнопками действий и проверкой прав - Добавить метод `table()` для AJAX-загрузки данных - Вызвать `parent::table()` для использования встроенной логики - Проверить права доступа перед вызовом родительского метода **В шаблоне:** - Подключить DataTable.js в блоке `scripts` - Инициализировать DataTable с корректным `id` и `url` - Передать `tableHtml` из контроллера в шаблон - Убедиться, что CSS стили таблицы подключены **Модель:** - Использовать трейт `TenantScopedModel` для автоматической фильтрации по организации - Убедиться, что модель имеет поле `organization_id` - Проверить, что модель возвращает данные в ожидаемом формате --- ## Типичные ошибки и их устранение ### Таблица не загружается Если данные не загружаются, проверьте следующее: - URL в конфигурации и при инициализации DataTable должны совпадать - Метод `table()` контроллера должен вызывать `parent::table()` - Модель должна использовать трейт `TenantScopedModel` или обрабатывать фильтрацию вручную - Проверьте консоль браузера на наличие ошибок JavaScript - Убедитесь, что CSRF-токен передаётся корректно ### Сортировка не работает Если сортировка не работает: - Поле должно быть указано в массиве `sortable` - Имя поля в `sortable` должно соответствовать ключу в `columns` и имени поля в базе данных - Для JOIN-запросов используйте алиасы таблиц в `sortable` (`c.name` вместо `client_name`) ### Поиск не работает Если поиск не работает: - Поле должно быть указано в массиве `searchable` - При использовании JOIN проверьте `fieldMap` для маппинга параметров - Убедитесь, что в контроллере используется метод `like()` для фильтрации ### Действия не отображаются Если кнопки действий не отображаются: - Проверьте `can_edit` и `can_delete` в конфигурации - Убедитесь, что `type` действия соответствует проверяемому праву (`'edit'` или `'delete'`) - Проверьте параметр `actions` в конфигурации (должен быть `{label: 'Действия'}` или `true`) --- ## Сводка параметров конфигурации | Параметр | Тип | Обязательный | Описание | |----------|-----|--------------|----------| | `id` | string | Да | Идентификатор контейнера таблицы | | `url` | string | Да | URL для AJAX-загрузки | | `model` | Model | Да | Экземпляр модели CodeIgniter | | `columns` | array | Да | Конфигурация колонок | | `searchable` | array | Нет | Поля для поиска | | `sortable` | array | Нет | Поля для сортировки | | `defaultSort` | string | Нет | Поле сортировки по умолчанию | | `order` | string | Нет | Направление сортировки по умолчанию | | `actions` | array\|bool | Нет | Конфигурация колонки действий | | `actionsConfig` | array | Нет | Кнопки действий | | `emptyMessage` | string | Нет | Сообщение при отсутствии данных | | `emptyIcon` | string | Нет | Иконка при пустом состоянии | | `emptyActionUrl` | string | Нет | URL действия при пустом состоянии | | `emptyActionLabel` | string | Нет | Текст кнопки действия | | `can_edit` | bool | Нет | Разрешено ли редактирование | | `can_delete` | bool | Нет | Разрешено ли удаление | | `fieldMap` | array | Нет | Маппинг параметров фильтрации | | `scope` | callable | Нет | Кастомный запрос к базе данных |