Skip to content

Form utils

codecannon apps ship with some helpers to help you build forms more easily.

useForm

The useForm hook is a helper that helps you manage form state and validation.

For useForm, we won't go into the complete implementation here as it's quite complex, but we'll show you how to use it.

You can always find the complete composable in ui/src/helpers/form.ts

Basic usage

Minimal usage example of the useForm composable:

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading } = useForm({
	api: () => new UsersApi(),
	defaultData: () => ({
		email: '',
	}),
})

The useForm hook takes an object with the following properties:

  • api: The API class that the form will use to submit data.
  • defaultData: A function that returns the default form data.

The useForm composable returns an object with the following properties:

Editing

If you're editing an existing resource, you can pass the isEdit and id properties to the useForm composable. Doing so will load the entity when the form is created, and will allow you to update the entity using the submit function.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
    isEdit: () => true,
    id: () => 1,
})

Submitting

To submit the form, you can call the submit function that the useForm composable returns. Depending on if isEdit is true, the function will either create or update the entity.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading, submit } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
})

const handleSubmit = async () => {
    await submit()
}

Validation

The useForm composable automatically works with Laravel backend validation.

If the backend returns validation errors, the formErrors object will be populated with the errors. The keys in the formErrors object correspond to the form fields, while the value will be the first field error.

You can pass the form errors to the FormInput commponent to display the errors.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading, formErrors, submit } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
})

const handleSubmit = async () => {
    await submit()
    if (Object.keys(formErrors).length > 0) {
        console.log('Form has errors')
    }
}

Resetting

You can reset the form data by calling the reset function that the useForm composable returns.

If the form is in edit mode, the form data will be reset to the original entity data. If the form is not in edit mode, the form data will be reset to the default data defined in the defaultData property.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading, reset } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
})

const handleReset = () => {
    reset()
}

Removing

If you want to remove an entity, you can call the remove function that the useForm composable returns.

The user will be prompted to confirm the removal, and if they confirm, the entity will be removed.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading, remove } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
    isEdit: () => true,
    id: () => 1,
})

const handleRemove = async () => {
    await remove()
}

Redirect protection

If you want to protect the user from accidentally navigating away from the form when they have unsaved changes, you can set the protectNavigation property to true (true is the default state).

When navigation protection is enabled, the user will be prompted to confirm if they want to navigate away from the form when they have unsaved changes.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading, protectNavigation } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
})

const handleNavigation = () => {
    protectNavigation.value = false
}

Attaching to other entities

If you want to attach the form to another entity, when the form is submitted you can pass the attachTo property to the useForm composable.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
    attachTo: () => ({
      post: { method: 'associate', id: 1 },
    }},
})

Forcing Values

If you want to force a value for a field, you can pass the forceValues property to the useForm composable.

This is useful when you want to set a value for a field that is not in the form or hidden in the form.

typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
    forceValues: () => ({
      apartment_id: 1,
    }),
})

Hooks

The useForm composable also provides hooks that you can use to perform actions when certain events occur.

Hooks available are:

  • onStartLoading - Called when the form enters the loading state.
  • onStopLoading - Called when the form finishes the loading state.
  • onClose - Called when the form stops submitting.
  • onCreated - Called when the form creates a new entity.
  • onUpdated - Called when the form updates an entity.
  • onDeleted - Called when the form removes an entity.
typescript
import UsersApi from '@/models/User/Api'
import { useForm } from '@/helpers/form'

const { formData, loading, submit } = useForm({
    api: () => new UsersApi(),
    defaultData: () => ({
        email: '',
    }),
    onStartLoading: () => {
        console.log('Form is loading')
    },
    onStopLoading: () => {
        console.log('Form has stopped loading')
    },
    onClose: () => {
        console.log('Form has closed')
    },
    onCreated: () => {
        console.log('Entity has been created')
    },
    onUpdated: () => {
        console.log('Entity has been updated')
    },
    onDeleted: () => {
        console.log('Entity has been deleted')
    },
})