Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/src/docs/features/asynchronicity.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,36 @@ Notes:
- Turbo sends the Turbo-Frame header with the frame id; the bundle reads it for you. You don't need to access headers directly.
- The trait requires Twig to be available in your controller service (it is auto-wired by Symfony via the #[Required] setter).

## Asynchronous Data Table Loading

You can enable asynchronous loading for your data tables using the `async` option. This feature is especially useful for tables with slow data sources or when displaying multiple tables on a single page.

### Enabling Asynchronous Loading

To enable asynchronous loading, set the async option to true in your data table type:
```php
class ProductDataTableType extends AbstractDataTableType
{
// ...
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'async' => true,
]);
}
}
```

With this option enabled, the data table will not load its content immediately when the page is rendered. Instead, it will trigger a backend request to fetch and display the table data.

### How It Works

- When 'async' is enabled, the table's initial HTML does not include its data rows.
- The table content is loaded asynchronously via a backend call after the page loads.
- If the data table is not visible in the user's viewport, its content is not loaded until the user scrolls to it. This optimizes performance, especially on pages with multiple or heavy tables.
- If you need the table to load immediately regardless of visibility, keep async set to false (the default).


## Prefetching

<TurboPrefetchingSection>
Expand Down
12 changes: 11 additions & 1 deletion src/DataTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use Kreyu\Bundle\DataTableBundle\Personalization\Form\Type\PersonalizationDataType;
use Kreyu\Bundle\DataTableBundle\Personalization\PersonalizationData;
use Kreyu\Bundle\DataTableBundle\Query\ProxyQueryInterface;
use Kreyu\Bundle\DataTableBundle\Query\ResultSet;
use Kreyu\Bundle\DataTableBundle\Query\ResultSetInterface;
use Kreyu\Bundle\DataTableBundle\Sorting\SortingData;
use Symfony\Component\Form\FormBuilderInterface;
Expand Down Expand Up @@ -670,7 +671,16 @@ public function getPagination(): PaginationInterface

private function getResultSet(): ResultSetInterface
{
return $this->resultSet ??= $this->query->getResult();
if (
!$this->config->isAsync()
|| $this->isRequestFromTurboFrame()
) {
return $this->resultSet ??= $this->query->getResult();
}

// In this case, we don't want to fetch the results immediately,
// but rather return an empty result set that will be filled later through AJAX.
return new ResultSet(new \ArrayIterator([]), 0, 0);
}

private function createPagination(): PaginationInterface
Expand Down
5 changes: 5 additions & 0 deletions src/DataTableConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -876,4 +876,9 @@ private function createBuilderLockedException(): BadMethodCallException
{
return new BadMethodCallException('DataTableConfigBuilder methods cannot be accessed anymore once the builder is turned into a DataTableConfigInterface instance.');
}

public function isAsync(): bool
{
return $this->options['async'];
}
}
2 changes: 2 additions & 0 deletions src/DataTableConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,6 @@ public function getFiltrationParameterName(): string;
public function getPersonalizationParameterName(): string;

public function getExportParameterName(): string;

public function isAsync(): bool;
}
32 changes: 20 additions & 12 deletions src/Resources/views/themes/base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,27 @@
{% set stimulus_controllers = stimulus_controllers|merge(['kreyu--data-table-bundle--batch']) %}
{% endif %}

<turbo-frame id="kreyu_data_table_{{ name }}" target="_top">
<div
data-controller="{{ stimulus_controllers|join(' ') }}"
data-kreyu--data-table-bundle--state-url-query-parameters-value="{{ url_query_parameters|default({})|json_encode(constant('JSON_FORCE_OBJECT')) }}"
>
{{ block('action_bar', theme) }}
{{ block('table', theme) }}
{% if data_table.vars.is_async and not data_table.vars.is_request_from_turbo_frame %}
<turbo-frame id="kreyu_data_table_{{ name }}" src="{{ app.request.uri }}" loading="lazy">
<div>
<progress style="width: 100%">{{ 'Loading'|trans({}, 'KreyuDataTable') }}</progress>
</div>
</turbo-frame>
{% else %}
<turbo-frame id="kreyu_data_table_{{ name }}" target="_top">
<div
data-controller="{{ stimulus_controllers|join(' ') }}"
data-kreyu--data-table-bundle--state-url-query-parameters-value="{{ url_query_parameters|default({})|json_encode(constant('JSON_FORCE_OBJECT')) }}"
>
{{ block('action_bar', theme) }}
{{ block('table', theme) }}

{% if pagination_enabled %}
{{ data_table_pagination(pagination) }}
{% endif %}
</div>
</turbo-frame>
{% if pagination_enabled %}
{{ data_table_pagination(pagination) }}
{% endif %}
</div>
</turbo-frame>
{% endif %}
{% endblock %}

{% block kreyu_data_table_form_aware %}
Expand Down
4 changes: 4 additions & 0 deletions src/Type/DataTableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public function buildView(DataTableView $view, DataTableInterface $dataTable, ar
'sorting_clearable' => $dataTable->getConfig()->isSortingClearable(),
'has_batch_actions' => !empty($dataTable->getBatchActions()),
'per_page_choices' => $options['per_page_choices'],
'is_request_from_turbo_frame' => $dataTable->isRequestFromTurboFrame(),
'is_async' => $dataTable->getConfig()->isAsync(),
]);

$view->headerRow = $this->createHeaderRowView($view, $dataTable, $visibleColumns);
Expand Down Expand Up @@ -186,6 +188,7 @@ public function configureOptions(OptionsResolver $resolver): void
'personalization_form_factory' => $this->defaults['personalization']['form_factory'] ?? null,
'exporting_enabled' => $this->defaults['exporting']['enabled'] ?? false,
'exporting_form_factory' => $this->defaults['exporting']['form_factory'] ?? null,
'async' => $this->defaults['async'] ?? false,
])
->setAllowedTypes('title', ['null', 'string', TranslatableInterface::class])
->setAllowedTypes('title_translation_parameters', ['array'])
Expand Down Expand Up @@ -217,6 +220,7 @@ public function configureOptions(OptionsResolver $resolver): void
->setAllowedTypes('personalization_form_factory', ['null', FormFactoryInterface::class])
->setAllowedTypes('exporting_enabled', 'bool')
->setAllowedTypes('exporting_form_factory', ['null', FormFactoryInterface::class])
->setAllowedTypes('async', ['bool'])
;
}

Expand Down