Skip to content

Components

codecannon apps create a set of reusable components that can be used to build the frontend of your application. These components are designed to be used with Vue 3 and PrimeVue.

ModelSelect

ModelSelect is a component that allows the user to select a entity from a list of entities for a particular model. The list of models is fetched from the server.

The ModelSelect component is a wrapper around the PrimeVue Select commponent, so you can pass the Select props to the ModelSelect component.

vue
<template>
  <ModelSelect
      v-model="form.app_id"
      :api="new UsersApi()"
      option-label="name" />
</template>

<script>
import { ModelSelect } from '@/components'
import AppsApi from '@/models/Users/Api'

Model Select

FormInput

FormInput is a form input wrapper commponent that allows you to easily add a label and error text to a form input. It also allows you to mark an input as required.

vue
<template>
  <FormInput
      label="Email"
      required
      :errors="formErrors.email">
      <InputText v-model="form.email" />
  </FormInput>

</template>

<script>
import FormInput from '@/components/FormInput.vue'
import InputText from 'primevue/inputtext'

Form Input

Container

Container is a wrapper component that allows you to add default sizing to your app views.

vue
<template>
	<div class="ui-container">
		<slot></slot>
	</div>
</template>

<style scoped lang="scss">
.ui-container {
	width: 100%;
	padding: 24px 10%;
}
</style>

FormContainer

The FormContainer component is a wrapper component that adds forms the ability to be rendered either as a card or as a dialog.

vue
<template>
	<Dialog
		v-if="asDialog"
		:visible="visible"
		modal
		class="form-container"
		:header="title"
		@update:visible="emit('close')">
		<slot />
	</Dialog>
	<Card
		v-else
		border
		class="form-container">
		<template #title>
			{{ title }}
		</template>
		<template #content>
			<slot />
		</template>
	</Card>
</template>

<script setup lang="ts">
import Card from 'primevue/card'
import Dialog from 'primevue/dialog'

const emit = defineEmits(['close'])
withDefaults(
	defineProps<{
		asDialog?: boolean
		visible?: boolean
		title: string
	}>(),
	{
		asDialog: false,
		visible: false,
	},
)
</script>

<style lang="scss">
.form-container {
	width: 100%;
	max-width: 600px;
}
</style>

Usage as dialog

The FormContainer component can be used as a dialog by setting the asDialog prop to true. The dialog will be displayed when the visible prop is set to true.

vue
<template>
    <FormContainer
        asDialog
        :visible="visible"
        title="Create User"
        @update:visible="visible = $event">
        <!-- Form content -->
    </FormContainer>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import FormContainer from '@/components/FormContainer.vue'

const visible = ref(false)
</script>

By changing the visible prop, you can control the visibility of the dialog.

You can listen to the visible status of the FormContainer component by listening to the update:visible event.

Usage as card

The FormContainer component can be used as a card by setting the asDialog prop to false. The card will be displayed with the title provided.

vue
<template>
    <FormContainer
        title="Create user">
        <!-- Form content -->
    </FormContainer>
</template>

ApiTable

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.

vue
<template>
	<ApiTable
		:list-state="listState">
		<Column
			field="title"
			header="Title" />
		<Column
			field="body"
			header="Body" />
	</ApiTable>
</template>

<script setup lang="ts">
import { onBeforeMount } from 'vue'
import ApiTable from '@/components/Table/ApiTable.vue'
import Column from 'primevue/column'
import { usePostListState } from '@/models/Post/States'

const listState = usePostListState()
const router = useRouter()

onBeforeMount(() => {
	listState.getList()
})
</script>

The example above is how the table is used in List views. The ApiTable component is used to display a list of posts. The Column component is used to define the columns of the table. The ApiTableRemoveButton component is used to remove a post from the list.

Table data

The ApiTable component requires a listState prop to be passed in. The listState prop is an instance of the ListState class. It allows the ApiTable component to manage data fetching, loading states, and pagination interactions.

The data is not automatically loaded on mount, so you have to call the getList method on the listState instance to fetch the data. This is so you can control when the data is fetched.

Since the API table works with ListState, all requests will be made with the default parameters or saved queries on the ListState instance.

Pagination

The ApiTable component uses the Paginator component from PrimeVue to handle pagination. The Paginator component is automatically rendered at the bottom of the table. The ApiTable automatically handles pagintation state using the ListState instance provided.

Row click

The ApiTable component emits a row-click event when a row is clicked. The event is emitted with the row data as the payload. This allows you to handle click events on rows in the table.

vue
<template>
	<ApiTable
		:list-state="listState"
		@row-click="openDetails">
		<Column
			field="title"
			header="Title" />
		<Column
			field="body"
			header="Body" />
	</ApiTable>
</template>

<script setup lang="ts">
import { useRouter } from 'vue-router'
import ApiTable from '@/components/Table/ApiTable.vue'
import Column from 'primevue/column'
import { usePostListState } from '@/models/Post/States'
import type { Post } from '@/models/Post/Model'

const listState = usePostListState()
const router = useRouter()

function openDetails(item: { data: Post }) {
	router.push({ name: 'posts-edit', params: { id: item.data.id } })
}
</script>

Selection

The ApiTable component supports row selection. You can enable row selection by setting the selectionMode prop to 'single' or 'multiple'. The selected rows are stored in the selected prop as a reactive set.

You can pass a selected prop to the ApiTable component to control the selected rows from the parent component.

vue
<template>
    <ApiTable
        :list-state="listState"
        :selected="selected"
        selection-mode="multiple">
        <Column
            field="title"
            header="Title" />
        <Column
            field="body"
            header="Body" />
    </ApiTable>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { usePostListState } from '@/models/Post/States'
import type { Post } from '@/models/Post/Model'

const listState = usePostListState()
const selected = ref<Set<Post>>(new Set())
</script>

Loading

The ApiTable component automatically handles loading states. The loading prop can be used to override the loading state of the table should you need it.

If the loading prop is set to true, the table will display a loading indicator.

If the listState loading state is set to true, the table will display a loading indicator regardless of the loading prop.

vue
<template>
    <ApiTable
        :list-state="listState"
        :loading="loading">
        <Column
            field="title"
            header="Title" />
        <Column
            field="body"
            header="Body" />
    </ApiTable>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { usePostListState } from '@/models/Post/States'

const listState = usePostListState()
const loading = ref(false)
</script>

ApiTableRemoveButton

The ApiTableRemoveButton component is a button that allows you to remove a row from the table. The component is designed to be used inside the ApiTable component.

vue
<template>
	<ApiTable
		:list-state="listState">
		<Column
			field="title"
			header="Title" />
		<Column
			field="body"
			header="Body" />
		<Column header="">
			<template #body="slotProps">
				<ApiTableRemoveButton :item="slotProps.data" />
			</template>
		</Column>
	</ApiTable>
</template>

<script setup lang="ts">
import { onBeforeMount } from 'vue'
import ApiTable from '@/components/Table/ApiTable.vue'
import Column from 'primevue/column'
import ApiTableRemoveButton from '@/components/Table/ApiTableRemoveButton.vue'
import { usePostListState } from '@/models/Post/States'

const listState = usePostListState()

onBeforeMount(() => {
	listState.getList()
})
</script>

The ApiTableRemoveButton component requires an item prop to be passed in. The item prop is the entity that the button will remove via the API.

The user will be prompted with a confirmation dialog before the item is removed.