Datatable
The resources/js/Components/DataTable directory contains Vue components related to the AppDataTable Vue component, which is responsible for rendering tabular data, usually sourced from a database.
AppDataTable
The AppDataTable Vue component is generally used with the following components:
AppDataSearch: Responsible for rendering the search bar.AppDataTableHead: Responsible for rendering the table head.AppDataTableRow: Responsible for rendering the table rows.AppDataTableData: Responsible for rendering the table data.AppPaginator: Responsible for rendering the pagination links, if applicable.
The AppDataTable component can be used as follows:
<AppDataSearch
v-if="customers.data.length || route().params.searchTerm"
:url="route('customer.index')"
fields-to-search="name"
></AppDataSearch>
<AppDataTable v-if="customers.data.length" :headers="headers">
<template #TableBody>
<tbody>
<AppDataTableRow
v-for="customer in customers.data"
:key="customer.id"
>
<AppDataTableData>
{{ customer.id }}
</AppDataTableData>
<AppDataTableData>
{{ customer.name }}
</AppDataTableData>
<AppDataTableData>
<AppButton
class="btn btn-icon btn-primary"
@click="
$inertia.visit(
route(
'customer.edit',
customer.id
)
)
"
>
<i class="ri-edit-line"></i>
</AppButton>
</AppDataTableData>
</AppDataTableRow>
</tbody>
</template>
</AppDataTable>
<AppPaginator
:links="customers.links"
class="mt-4 justify-center"
></AppPaginator>
<AppAlert v-if="!customers.data.length" class="mt-4">
No customers found.
</AppAlert>Your Controller in modules/Customer/Http/Controllers/CustomerController.php will look like this:
<?php
namespace Modules\Customer\Http\Controllers;
use Modules\Support\Http\Controllers\BackendController;
use Modules\Customer\Models\Customer;
use Inertia\Response;
class CustomerController extends BackendController
{
public function index(): Response
{
$customers = Customer::orderBy('id')
->search(request('searchContext'), request('searchTerm'))
->paginate(request('rowsPerPage', 15))
->withQueryString()
->through(fn ($customer) => [
'id' => $customer->id,
'name' => $customer->name
]);
return inertia('Customer/CustomerIndex', [
'customers' => $customers
]);
}
}The customers prop that is passed to resources/js/Pages/Customer/CustomerIndex.vue, which uses the AppDataTable component, will look like this:
{
"current_page": 1,
"data": [
{ "id": 1, "name": "Customer One" },
{ "id": 2, "name": "Customer Two" }
],
"first_page_url": "/customer?page=1",
"from": 1,
"last_page": 2,
"last_page_url": "/customer?page=2",
"links": [
{ "url": null, "label": "« Previous", "active": false },
{
"url": "/customer?page=1",
"label": "1",
"active": true
},
{
"url": "/customer?page=2",
"label": "2",
"active": false
},
{
"url": "/customer?page=2",
"label": "Next »",
"active": false
}
],
"next_page_url": "/customer?page=2",
"path": "/customer",
"per_page": 2,
"prev_page_url": null,
"to": 2,
"total": 3
}Note that the customers prop contains the customers' data in the data key, along with pagination-related information in other keys.
<script setup>
import { ref } from 'vue'
const props = defineProps({
customers: {
type: Object,
default: () => {}
}
})
const headers = ['ID', 'Name', 'Actions']
</script>The rendered table will look like this (search, pagination links, and edit button are disabled in this example):
AppDataTable Props:
| Name | Type | Default | Description |
|---|---|---|---|
headers | Array | [] | An array containing the header names for the table. This can be used as an alternative (shorter syntax) to the more versatile TableHead slot of the AppDataTable. |
AppAlert Slots
| Name | Description |
|---|---|
TableHead | The Head of the AppDataTable. |
TableBody | The Body of the AppDataTable. |
AppDataSearch
The AppDataSearch Vue component is responsible for rendering the search feature related to the Data Table. It is designed to be integrated into your AppDataTable Vue Component and your Laravel backend with minimal code. The AppDataSearch component can be used as follows:
<AppDataSearch
v-if="customers.data.length || route().params.searchTerm"
:url="route('customer.index')"
fields-to-search="name"
/><script setup>
const props = defineProps({
customers: {
type: Object,
default: () => {}
}
})
</script>Note that the customers prop is an instance of the Illuminate\Pagination\LengthAwarePaginator class. So, when it's passed to the AppDataTable component, the customers' data will be available through the customers.data path inside the template.
All the work of communicating with the backend is handled by the resources/js/Composables/useDataSearch.js composable, so there's no need to worry about it. This composable assists with:
- Sending the search request to the backend.
- Debouncing the search input to avoid unnecessary requests.
- Handling the search input state and the clear search button.
- Preventing issues with back button navigation and searching.
AppDataSearch Props:
| Name | Type | Default | Description |
|---|---|---|---|
url | String | - | The route path that loads the resource, usually your Module's Controller index method. |
fieldsToSearch | String | - | Comma-separated list of the fields to be searched in your Module's database table. |
additionalParams | Object | {} | Additional parameters that must be sent with the search request to the backend, for example: { active: true }. |
AppDataTableHead
The AppDataTableHead Vue component is responsible for rendering the table head of your AppDataTable Vue component. It's used internally by the AppDataTable component when the headers prop is passed to AppDataTable. The AppDataTableHead component can be used as follows:
<AppDataTable>
<template #TableHead>
<AppDataTableHead :headers="headers" />
</template>
</AppDataTable><script setup>
const headers = ['ID', 'Name', 'Actions']
</script>The rendered Table Head will look like this:
| ID | Name | Actions |
|---|
AppDataTableHead Props:
| Name | Type | Default | Description |
|---|---|---|---|
headers | Array | [] | An array containing the header names for the table. |
AppDataTableRow
The AppDataTableRow Vue component is responsible for rendering the table rows of your AppDataTable Vue component. It's used internally by the AppDataTable component inside the TableBody slot. Typically, you will use it with a v-for loop, iterating over the data that comes from the backend. The AppDataTableRow component can be used as follows:
<AppDataTable v-if="customers.data.length" :headers="headers">
<template #TableBody>
<tbody>
<AppDataTableRow
v-for="customer in customers.data"
:key="customer.id"
>
<AppDataTableData>
{{ customer.id }}
</AppDataTableData>
<AppDataTableData>
{{ customer.name }}
</AppDataTableData>
<AppDataTableData>
<AppButton
class="btn btn-icon btn-primary"
@click="
$inertia.visit(
route(
'customer.edit',
customer.id
)
)
"
>
<i class="ri-edit-line"></i>
</AppButton>
</AppDataTableData>
</AppDataTableRow>
</tbody>
</template>
</AppDataTable>
<AppAlert v-if="!customers.data.length" class="mt-4">
No customers found.
</AppAlert><script setup>
const props = defineProps({
customers: {
type: Object,
default: () => {}
}
})
const headers = ['ID', 'Name', 'Actions']
</script>The rendered result will look like this:
| ID | Name | Actions |
|---|---|---|
| 1 | Customer One | |
| 2 | Customer Two |
AppTableRow Slots
| Name | Description |
|---|---|
default | The content of the Table Row (<tr> element). |
AppDataTableData
The AppDataTableData Vue component is responsible for rendering the table data of your AppDataTable Vue component. It's used internally by the AppDataTableRow component inside the TableBody slot. The AppDataTableData component can be used as follows:
<AppDataTable v-if="customers.data.length" :headers="headers">
<template #TableBody>
<tbody>
<AppDataTableRow
v-for="customer in customers.data"
:key="customer.id"
>
<AppDataTableData>
{{ customer.id }}
</AppDataTableData>
<AppDataTableData>
{{ customer.name }}
</AppDataTableData>
<AppDataTableData>
<AppButton
class="btn btn-icon btn-primary"
@click="
$inertia.visit(
route(
'customer.edit',
customer.id
)
)
"
>
<i class="ri-edit-line"></i>
</AppButton>
</AppDataTableData>
</AppDataTableRow>
</tbody>
</template>
</AppDataTable><script setup>
const props = defineProps({
customers: {
type: Object,
default: () => {}
}
})
const headers = ['ID', 'Name', 'Actions']
</script>The rendered result will look like this:
| ID | Name | Actions |
|---|---|---|
| 1 | Customer One | |
| 2 | Customer Two |
AppDataTableData Slots
| Name | Description |
|---|---|
default | The content of the Table Data (<td> element). |
AppPaginator
The AppPaginator Vue component is responsible for rendering the pagination links of your AppDataTable Vue component. The AppPaginator component can be used as follows:
<AppPaginator
:links="customers.links"
class="mt-4 justify-center"
></AppPaginator><script setup>
const props = defineProps({
customers: {
type: Object,
default: () => {}
}
})
</script>Your Controller in modules/Customer/Http/Controllers/CustomerController.php will look like this:
<?php
namespace Modules\Customer\Http\Controllers;
use Modules\Support\Http\Controllers\BackendController;
use Modules\Customer\Models\Customer;
use Inertia\Response;
class CustomerController extends BackendController
{
public function index(): Response
{
$customers = Customer::orderBy('id')
->search(request('searchContext'), request('searchTerm'))
->paginate(request('rowsPerPage', 15))
->withQueryString()
->through(fn ($customer) => [
'id' => $customer->id,
'name' => $customer->name
]);
return inertia('Customer/CustomerIndex', [
'customers' => $customers
]);
}
}Note that in the Controller, the $customers variable is an instance of the Illuminate\Pagination\LengthAwarePaginator class. It contains the customers' data in the data key, and pagination-related information in other keys.
As mentioned earlier in this documentation, the customers prop received in the resources/js/Pages/Customer/CustomerIndex.vue component, which utilizes the AppPaginator component, will look like this:
{
"current_page": 1,
"data": [
{ "id": 1, "name": "Customer One" },
{ "id": 2, "name": "Customer Two" }
],
"first_page_url": "/customer?page=1",
"from": 1,
"last_page": 2,
"last_page_url": "/customer?page=2",
"links": [
{ "url": null, "label": "« Previous", "active": false },
{
"url": "/customer?page=1",
"label": "1",
"active": true
},
{
"url": "/customer?page=2",
"label": "2",
"active": false
},
{
"url": "/customer?page=2",
"label": "Next »",
"active": false
}
],
"next_page_url": "/customer?page=2",
"path": "/customer",
"per_page": 2,
"prev_page_url": null,
"to": 2,
"total": 3
}The rendered result will look like this (pagination links are disabled in this example):
AppPaginator Props:
| Name | Type | Default | Description |
|---|---|---|---|
links | Array | [] | An array of Objects containing the links to build the paginator, like [{'url': '/customer?page=1', label: '1', active: true}]. |