Validation
Learn how to start using Precognition form validation in your Nuxt projects!
Laravel Precognition allows you to anticipate the outcome of a future HTTP request. One of the primary use cases of Precognition is the ability to provide "live" validation for your frontend JavaScript application without having to duplicate your application's backend validation rules.
You can also check the official Laravel documentation for more details about this feature - Laravel Precognition.
Live validation
First, to enable Precognition for a route, the HandlePrecognitiveRequests
middleware should be added to the route definition. You should also create a form request to house the route's validation rules:
use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
With nuxt-sanctum-precognition
module installed, you can now create a form object using Precognition's usePrecognitionForm
composable, providing the HTTP method (post
), the target URL (/users
), and the initial form data.
type ProfileUpdateForm = {
email: string
password: string
}
const payload: ProfileUpdateForm = {
email: '',
password: '',
}
const form = usePrecognitionForm<ProfileUpdateForm>('post', '/profile', payload)
Then, to enable live validation, invoke the form's validate
method on each input's change
event, providing the input's name:
<script setup lang="ts">
import type { FetchResponse } from 'ofetch'
type RegistrationForm = {
name: string
email: string
}
const payload: RegistrationForm = {
email: '',
name: '',
}
const form = usePrecognitionForm<RegistrationForm>('post', '/users', payload)
const submit = () => form
.submit()
.then((response: FetchResponse<unknown>) => console.log('Form submitted', response))
.catch((error: FetchResponse<unknown>) => console.error('Form error', error))
</script>
<template>
<form @submit.prevent="submit">
<label for="name">Name</label>
<input
id="name"
v-model="form.fields.name"
@change="form.validate('name')"
>
<div v-if="form.invalid('name')">
{{ form.errors.name }}
</div>
<label for="email">Email</label>
<input
id="email"
v-model="form.fields.email"
type="email"
@change="form.validate('email')"
>
<div v-if="form.invalid('email')">
{{ form.errors.email }}
</div>
<button :disabled="form.processing">
Create User
</button>
</form>
</template>
Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by changing the validationTimeout
config in nuxt.config.ts
:
export default defineNuxtConfig({
// ... other config
precognition: {
validationTimeout: 1500,
},
})
When a validation request is in-flight, the form's validating
property will be true
:
<div v-if="form.validating">
Validating...
</div>
Any validation errors returned during a validation request or a form submission will automatically populate the form's errors
object:
<div v-if="form.invalid('email')">
{{ form.errors.email }}
</div>
You can determine if the form has any errors using the form's hasErrors
property:
<div v-if="form.hasErrors">
<!-- ... -->
</div>
You may also determine if an input has passed or failed validation by passing the input's name to the form's valid
and invalid
functions, respectively:
<span v-if="form.valid('email')">
✅
</span>
<span v-else-if="form.invalid('email')">
❌
</span>
If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's forgetError
function to achieve this:
<input
id="avatar"
type="file"
@change="(e) => {
form.avatar = e.target.files[0]
form.forgetError('avatar')
}"
>
As we have seen, you can hook into an input's change
event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step.
To do this with Precognition, you should call the validate
method passing the field names you wish to validate to the only
configuration key. You may handle the validation result with onSuccess
, onError
or onValidationError
callbacks:
<button
type="button"
@click="form.validate(
['name', 'email', 'phone'],
{
onSuccess: (response: FetchResponse<unknown>) => /* ... */,
onError: (error) => /* ... */,
onValidationError: (response: FetchResponse<unknown>) => /* ... */,
}
)"
>Next Step</button>
Of course, you may also execute code in reaction to the response to the form submission. The form's submit
function returns an ofetch
response promise. This provides a convenient way to access the response payload, reset the form inputs on successful submission, or handle a failed request:
const submit = () => form
.submit()
.then((response: FetchResponse<unknown>) => {
form.reset()
alert('User created.')
})
.catch((error: FetchResponse<unknown>) => alert('An error occurred.'))
There is a way also to leverage async-await syntax to submit the form:
async function submit() {
try {
const response = await form.submit()
form.reset()
alert('User created.')
}
catch (e) {
const response = e as FetchResponse<unknown>
alert(`An error occurred - ${response.status}.`)
}
}
You may determine if a form submission request is in-flight by inspecting the form's processing
property:
<button :disabled="form.processing">
Submit
</button>
This overview covers most of the use cases. For more details about the composable or error handling, click on the corresponding link!
Last updated