Skip to content

useListConnector

useListConnector wraps a useAsyncData list composable and provides everything a data table needs: rows, loading state, pagination, row selection, and action triggers to coordinate with forms and delete modals.

API reference

State

PropertyTypeDescription
table.rowsComputedRef<any[]>The list of items. Unwraps both item[] and { data: item[] } response shapes automatically.
table.loadingComputedRef<boolean>true while the list request is in flight.
table.errorComputedRef<any>The error from the last failed request, or null.
table.columnsComputedRef<ColumnDef[]>Column definitions inferred from the OpenAPI schema, with any label overrides applied.

Column label translation

The generator infers column labels from the field names in your OpenAPI spec. You can override them from the component — safe from regenerations.

Option 1: static map — override specific labels by field key:

ts
const { table } = usePetsConnector(
  { status: 'available' },
  {
    columnLabels: {
      name: 'Nombre',
      status: 'Estado',
      createdAt: 'Fecha de alta',
    },
  }
)

Option 2: i18n function — applied to every column key:

ts
const { t } = useI18n()

const { table } = usePetsConnector(
  {},
  { columnLabel: (key) => t(`pets.columns.${key}`) }
)

Priority: columnLabel function > columnLabels map > generated label.

Pagination

Only available when the list endpoint has a defined response schema and paginated: true is set (the generator sets this automatically).

PropertyTypeDescription
table.paginationComputedRef<PaginationState | null>Current pagination state, or null if not paginated.
table.pagination.value.currentPagenumber
table.pagination.value.totalPagesnumber
table.pagination.value.totalnumber
table.pagination.value.perPagenumber
table.pagination.value.hasNextPageboolean
table.pagination.value.hasPrevPageboolean
table.nextPage()functionGo to next page. No-op if already on the last page.
table.prevPage()functionGo to previous page. No-op if already on the first page.
table.goToPage(n)functionJump to a specific page number.
table.setPerPage(n)functionChange page size and reset to page 1.

Row selection

PropertyTypeDescription
table.selectedRef<any[]>Currently selected rows.
table.onRowSelect(row)functionToggle-selects a row (adds if not selected, removes if selected).
table.clearSelection()functionClears the selection.

Actions

PropertyTypeDescription
table.refresh()functionRe-fetches the list. Call this after a successful create, update, or delete.
table.create()functionSignals the parent to open a create form. Increments _createTrigger.
table.update(row)functionSignals the parent to open an edit form for row. Sets _updateTarget.
table.remove(row)functionSignals the parent to open a delete confirmation for row. Sets _deleteTarget.

Coordination refs

These are the reactive values your component watches to react to table actions:

PropertyTypeDescription
table._createTriggerRef<number>Increments each time table.create() is called. Watch to open a create modal.
table._updateTargetShallowRef<any | null>Set to the row each time table.update(row) is called. Watch to open an edit modal.
table._deleteTargetShallowRef<any | null>Set to the row each time table.remove(row) is called. Watch to open a delete modal.

Examples

Basic table (no params)

vue
<script setup>
const { table } = usePetsConnector()
</script>

<template>
  <div v-if="table.loading">Loading...</div>
  <div v-else-if="table.error">Failed to load pets.</div>

  <table v-else>
    <tbody>
      <tr v-for="row in table.rows" :key="row.id">
        <td>{{ row.name }}</td>
        <td>{{ row.status }}</td>
      </tr>
    </tbody>
  </table>
</template>

With static query params

vue
<script setup>
// TypeScript will autocomplete and validate the params object
const { table } = usePetsConnector({ status: 'available' })
</script>

With a factory (different endpoint or composable options)

Use a factory when you need to call a different endpoint than the one the generator picked, or when you need composable-level options like lazy:

vue
<script setup>
// Use a different list endpoint
const { table } = usePetsConnector(
  () => useAsyncDataFindPetsByStatus({ status: 'pending' })
)

// Use the generated endpoint but with composable options
const { table } = usePetsConnector(
  () => useAsyncDataFindPetsByTags({ tags: ['cat', 'dog'] }, { lazy: true })
)
</script>

Table with pagination

vue
<script setup>
const { table } = usePetsConnector({ status: 'available' })
</script>

<template>
  <table>
    <tbody>
      <tr v-for="row in table.rows" :key="row.id">
        <td>{{ row.name }}</td>
      </tr>
    </tbody>
  </table>

  <div v-if="table.pagination">
    <button :disabled="!table.pagination.hasPrevPage" @click="table.prevPage()">← Prev</button>
    <span>Page {{ table.pagination.currentPage }} of {{ table.pagination.totalPages }}</span>
    <button :disabled="!table.pagination.hasNextPage" @click="table.nextPage()">Next →</button>
  </div>
</template>

Table with action buttons and CRUD wiring

vue
<script setup>
const showCreateModal = ref(false)
const showEditModal = ref(false)

const { table, createForm, updateForm, deleteAction, detail } = usePetsConnector({ status: 'available' })

watch(table._createTrigger, () => {
  createForm.reset()
  showCreateModal.value = true
})

watch(table._updateTarget, (row) => {
  if (row) {
    detail.load(row.id)
    showEditModal.value = true
  }
})

watch(table._deleteTarget, (row) => {
  if (row) deleteAction.setTarget(row)
})
</script>

<template>
  <button @click="table.create()">New Pet</button>

  <table>
    <tbody>
      <tr v-for="row in table.rows" :key="row.id">
        <td>{{ row.name }}</td>
        <td>
          <button @click="table.update(row)">Edit</button>
          <button @click="table.remove(row)">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>
</template>

Row selection

vue
<script setup>
const { table } = usePetsConnector()
</script>

<template>
  <p>{{ table.selected.length }} selected</p>
  <button @click="table.clearSelection()">Clear</button>

  <table>
    <tbody>
      <tr
        v-for="row in table.rows"
        :key="row.id"
        :class="{ selected: table.selected.includes(row) }"
        @click="table.onRowSelect(row)"
      >
        <td>{{ row.name }}</td>
      </tr>
    </tbody>
  </table>
</template>

Released under the Apache-2.0 License.