ComfyCoders TranquilTools
Docs /Laravel Vue TableBuilder
Repository
v1

HTTP Transport

By default TableBuilder drives its state through the url and lets Inertia reload the page props. With transport="http" it fetches its own rows from source instead and keeps sort, filter, search, per-page and page state in memory. The url is never touched, so the table can sit on a page that has nothing to do with the data it shows.

Use it for a table fed by an external API, for a table inside a modal or tab that must not navigate, and for several independent tables on one page.

Usage


<script setup lang="ts">
    import {TableBuilder} from '@/components'
    import type {TableData} from '@/types/table-builder'

    defineProps<{ table: TableData }>()
</script>

<template>
    <TableBuilder
        :table="table"
        transport="http"
        source="https://api.example.com/v1/brands"
    />
</template>

The table prop is still required: it carries the columns, filters, search inputs and per-page options that the component renders. Only the rows and the pagination come from source. Serialize an empty builder for that:

return Inertia::render('Brands/Index', [
    'table' => BrandsTable::make([])->toArray(),
]);

The Request

The built-in request is a GET with Accept: application/json and these parameters:

Parameter Sent when
sort a column is sorted, prefixed with - for descending
filter[key] the filter has a value
filter[global] the search input has a value
page past the first page
perPage a per-page option is chosen

Table names never namespace these parameters. Each http table has its own state, so there is nothing to keep apart.

Expected Response

{
    "data": [{"id": 1, "name": "Acme"}],
    "pagination": {"current_page": 1, "last_page": 4, "per_page": 10, "total": 40, "from": 1, "to": 10}
}

A bare array is accepted as the rows without pagination, and meta is read when pagination is absent. Anything else needs an adapter.

Custom Request

fetcher replaces the request entirely. Use it for authentication headers, a different parameter naming or a client you already have:


<script setup lang="ts">
    import type {TableFetcher} from '@/composables'

    const fetcher: TableFetcher = async (query, source) => {
        const response = await fetch(`${source}?page=${query.page}&q=${query.search}`, {
            headers: {Authorization: `Bearer ${token}`},
        })

        if (!response.ok) {
            throw new Error(`Request failed with status ${response.status}`)
        }

        return response.json()
    }
</script>

<template>
    <TableBuilder :table="table" transport="http" source="/api/brands" :fetcher="fetcher"/>
</template>

A thrown error is shown above the table.

Custom Response Shape

adapter maps the response body onto the rows and the pagination:

const adapter: TableAdapter = (payload) => ({
    data: payload.results,
    pagination: {
        current_page: payload.page,
        last_page: payload.pages,
        per_page: payload.size,
        total: payload.count,
        from: null,
        to: null,
        links: [],
        first_page_url: null,
        last_page_url: null,
        next_page_url: null,
        prev_page_url: null,
    },
})

Escaping

Rows that come from source are rendered with v-html and are not escaped by the PHP builder. Escape them on the server, or render the column through a cell-{key} slot.

Row Actions

When the response carries a rowActions array next to data (the shape TableBuilder::toArray() returns), the default adapter passes it on and the row action buttons render for the fetched rows. Actions other than get are sent with fetch() and the table reloads afterwards. The row actions stay disabled until that fetch() settles; see Row Actions.

Totals and Export

A totals key in the response (the shape TableBuilder::toArray() returns) replaces the totals of the initial render; without it the footer is hidden, because totals of the first render would no longer match the rows. The export button appends the in-memory sort, search and filters to the export url in the query keys of the table, so the file matches what the table shows.

Bulk Actions

Bulk actions post {ids: [...]} to the action url with same-origin credentials and the X-XSRF-TOKEN header, so a Laravel web route accepts them. The url is still signed by the PHP builder. The package route answers such a request with {"performed": true} instead of a redirect back to the page. After a successful response the selection is cleared and the table refetches; after a failed one the selection stays and the first validation error or the message of the response is shown above the table.

Using the Composable Directly

useTableTransport holds all of this and is exported for custom table components:

import {useTableTransport} from '@tranquil-tools/laravel-vue-table-builder'

const {table, loading, error, sortFor, applySort, applyFilter, applySearch, applyPerPage, applyPage, reload} =
    useTableTransport({
        table: computed(() => props.table),
        transport: computed(() => props.transport),
        source: computed(() => props.source),
        name: computed(() => props.name ?? 'default'),
    })

Changing source or transport after mount reloads the table. A response that a newer request has overtaken is discarded, so fast typing cannot leave stale rows on screen.