Skip to content

ApiTable Component

The ApiTable component is designed for displaying paginated API data. It leverages PrimeVue components (such as DataTable, Paginator, and Card) to render a fully functional table with pagination support. By providing a listState instance, the component can manage data fetching, loading states, and pagination interactions automatically.

Component

vue
<template>
	<div class="ui-api-table">
		<Card class="ui-api-table__container-card">
			<template #content>
				<DataTable
					class="ui-api-table__table"
					:class="{ 'ui-api-table__table--clickable': props.clickable }"
					:value="listState.list.value"
					v-bind="$attrs"
					:selection-mode="props.selectionMode"
					@row-click="emit('row-click', $event)">
					<slot></slot>
				</DataTable>
				<Paginator
					class="mt-5"
					:model-value="props.listState.pagination.value.current_page"
					:rows="listState.pagination.value.per_page"
					:total-records="props.listState.pagination.value.total"
					@page="getList($event)"></Paginator>
			</template>
		</Card>
	</div>
</template>

<script lang="ts">
import DataTable from 'primevue/datatable'
import Paginator from 'primevue/paginator'
import Card from 'primevue/card'
import type { Model as ModelType } from '@/helpers/models/Model'

export interface ApiTableInjectionType<
	Api extends IApi<Model, ModelList>,
	Model extends ModelType,
	ModelList extends LaravelPaginationResponse<Model>,
> {
	listState: ListState<Api, Model, ModelList> | undefined
	selectable: boolean
	selected: Set<Model>
	isLoading: Ref<boolean>
	selectionMode?: 'single' | 'multiple'
}

const injectionSymbol = Symbol('ApiTableInject')

export function createInjectionKey<
	Api extends IApi<Model, ModelList>,
	Model extends ModelType,
	ModelList extends LaravelPaginationResponse<Model>,
>() {
	return injectionSymbol as InjectionKey<ApiTableInjectionType<Api, Model, ModelList>>
}
</script>

<script
	setup
	lang="ts"
	generic="
		Api extends IApi<Model, ModelList>,
		Model extends ModelType,
		ModelList extends LaravelPaginationResponse<Model>
	">
import {
	defineProps,
	computed,
	defineEmits,
	defineOptions,
	type PropType,
	type InjectionKey,
	provide,
	reactive,
	type Ref,
} from 'vue'
import ListState from '@/helpers/models/ListState'
import type { IApi } from '@/helpers/models/Api'
import type { LaravelPaginationResponse } from '@/interfaces/models/Laravel'

defineOptions({
	inheritAttrs: false,
})

const emit = defineEmits(['get-list', 'update:pagination-page', 'row-click'])

const props = defineProps({
	listState: {
		type: ListState<Api, Model, ModelList>,
		required: true,
	},
	loading: {
		type: Boolean,
		required: false,
	},
	selectable: {
		type: Boolean,
		default: false,
	},
	selected: {
		type: Set as PropType<Set<Model>>,
		default: () => reactive(new Set()),
	},
	maxHeight: {
		type: [Number, String],
		default: 'auto',
	},
	selectionMode: {
		type: String as PropType<'single' | 'multiple'>,
		default: undefined,
	},
	clickable: {
		type: Boolean,
		default: false,
	},
})

const isLoading = computed(() => {
	return props.loading || !!props.listState?.isLoading.value
})

function getList(page: { page: number }) {
	if (props.listState) {
		props.listState.getList({ page: page.page + 1 })
	}
	emit('update:pagination-page', page.page + 1)
}

const injectionKey = createInjectionKey<Api, Model, ModelList>()
const providedData: ApiTableInjectionType<Api, Model, ModelList> = {
	listState: props.listState,
	selectable: props.selectable,
	selected: props.selected,
	isLoading: isLoading,
}
provide(injectionKey, providedData)

const maxHeightPx = computed(() => {
	if (props.maxHeight === 'auto') {
		return 'none'
	}
	return `${props.maxHeight}px`
})
</script>

Generic Parameters

Api

  • Type: IApi<Model, ModelList>
  • Details The API instance used by the ApiTable must extend the generic API interface. It's type will be automatically inferred from the listState prop.

Model

  • Type: ModelType
  • Details The model type representing the data item. It's type will be automatically inferred from the listState prop.

ModelList

  • Type: LaravelPaginationResponse<Model>
  • Details The type representing the paginated response from the API. It's type will be automatically inferred from the listState prop.

Props

listState

  • Type: ListState<Api, Model, ModelList>
  • Required: true
  • Details: The central state and methods for managing the list data and pagination. This is usually defined in the parent component and passed down to the ApiTable, allowing you to control the list of entities in the table from the parent component.

loading

  • Type: Boolean
  • Required: false
  • Details: A boolean value indicating whether the table is in a loading state. When set to true, the table displays a loading spinner.

selectable

  • Type: Boolean
  • Default: false
  • Details: A boolean value indicating whether the table rows are selectable. When set to true, the table displays checkboxes for selecting rows.

selected

  • Type: Set<Model>
  • Default: new Set()
  • Details: A reactive set holding the selected items in the table. This prop is used to manage the selected items in the table and can be read or updated from the parent component.

maxHeight

  • Type: Number | String
  • Default: 'auto'
  • Details: The maximum height of the table. If set to 'auto', the table will expand to fit its content. Otherwise, the table will have a fixed height in px based on the provided numeric value. The maxHeight is injected into the table's styles.

selectionMode

  • Type: 'single' | 'multiple'
  • Default: undefined
  • Details: The selection mode for the table rows. When set to 'single', only one row can be selected at a time. When set to 'multiple', multiple rows can be selected.

clickable

  • Type: Boolean
  • Default: false
  • Details: A boolean value indicating whether the table rows are clickable. When set to true, the rows will have a pointer cursor and emit a row-click event when clicked.

Provide/Inject

The component uses an injection key to provide common state and methods to its child components (such as the ApiTableRemoveButton component):

listState

  • Type: ListState<Api, Model, ModelList> | undefined
  • Details: The central state and methods for managing the list data and pagination. This is usually defined in the parent component and passed down to the ApiTable, allowing you to control the list of entities in the table from the parent component.

selectable

  • Type: boolean
  • Details: A boolean value indicating whether the table rows are selectable.

selected

  • Type: Set<Model>
  • Details: A reactive set holding the selected items in the table.

isLoading

  • Type: Ref<boolean>
  • Details: A reactive reference to a boolean value indicating whether the table is in a loading state.

Usage Example

The ApiTable component is typically used in a few auto generated components.