How are you accessing the errors in the frontend?
Laravel + Inertia + Vue: Rendering Form Errors
I am making some edits to the Breeze register functionality, and requiring a phone number instead of an email address. I use a Phone number input component that provides me with various formats for the phone number after input.
Currently, we only want to support US phone numbers (phone numbers starting with 1 like so: 15555555555)
The form validation errors work for the first and last name inputs, but if there are any errors on the phone input, the form doesn't submit, but the errors don't show up either, and I'm not sure why?
Register.vue:
<script setup>
import GuestLayout from '@/Layouts/GuestLayout.vue';
import InputError from '@/Components/InputError.vue';
import InputLabel from '@/Components/InputLabel.vue';
import PrimaryButton from '@/Components/PrimaryButton.vue';
import TextInput from '@/Components/TextInput.vue';
import {Head, Link, useForm} from '@inertiajs/vue3';
import {ref} from "vue";
import MazPhoneNumberInput from 'maz-ui/components/MazPhoneNumberInput';
const form = useForm({
first_name: '',
last_name: '',
phone: ref({}),
});
const submit = () => {
form.post(route('register'), {
onFinish: () => form.reset('first_name', 'last_name', 'phone'),
});
};
</script>
<template>
<GuestLayout>
<Head title="Register"/>
<form @submit.prevent="submit">
<div>
<InputLabel for="first_name" value="First Name"/>
<TextInput
id="first_name"
type="text"
class="mt-1 block w-full"
v-model="form.first_name"
autofocus
autocomplete="first_name"
/>
<InputError class="mt-2" :message="form.errors.first_name"/>
</div>
<div>
<InputLabel for="last_name" value="Last Name"/>
<TextInput
id="last_name"
type="text"
class="mt-1 block w-full"
v-model="form.last_name"
required
autocomplete="last_name"
/>
<InputError class="mt-2" :message="form.errors.last_name"/>
</div>
<div class="mt-4">
<InputLabel for="phone" value="Phone Number"/>
<MazPhoneNumberInput
id="phone"
color="info"
:only-countries="['US']"
:no-country-selector="true"
@update="form.phone = $event"
:success="form.phone?.isValid"
/>
<InputError v-if="form.errors.phone" class="mt-2" :message="form.errors.phone"/>
</div>
<div class="flex items-center justify-end mt-4">
<Link
:href="route('login')"
class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Already registered?
</Link>
<PrimaryButton
class="ml-4"
:class="{ 'opacity-25': ((form.processing || !results?.isValid) && (form.first_name == null && form.last_name == null)) }"
:disabled="(form.processing || !results?.isValid) && (form.first_name == null && form.last_name == null)"
>Sign up
</PrimaryButton>
</div>
</form>
</GuestLayout>
</template>
RegisteredUserController::store()
public function store(RegisterRequest $request): RedirectResponse
{
$data = $request->validated();
ddd($data);
/* ... rest of code */
}
RegisterRequest (Form Request):
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Foundation\Http\FormRequest;
use JetBrains\PhpStorm\ArrayShape;
class RegisterRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'phone.e164' => ['required', 'unique:users,phone_e164'],
'phone.countryCallingCode' => ['required', 'numeric', 'digits:1', 'in:1'],
'phone.countryCode' => ['required', 'string', 'max:2'],
'phone.formatInternational' => ['required', 'string'],
'phone.formatNational' => ['required', 'string'],
'phone.isPossible' => ['required', 'boolean'],
'phone.isValid' => ['required', 'boolean'],
'phone.nationalNumber' => ['required', 'numeric', 'digits:10'],
'phone.rfc3966' => ['required', 'string'],
'phone.type' => ['required', 'string'],
'phone.uri' => ['required', 'string'],
];
}
#[ArrayShape(['phone.e164.unique' => 'string', 'phone.countryCallingCode.in' => 'string'])]
public function messages(): array
{
return [
'phone.e164.unique' => 'This phone number is already registered. Please log in.',
'phone.countryCallingCode.in' => 'Sorry, we only support US phone numbers at this time.',
];
}
}
I'm not sure why the validation errors for the phone are not showing up on the Vue Register page. The errors are being caught because we're never reaching the ddd() method, but the errors aren't showing up on Vue either.
Edit: I pasted the Form request code twice. Updated with the correct code.
You're trying to display form.errors.phone, but you will not get that. Instead you will get, form.errors.phone.e164, form.errors.phone.countryCallingCode, form.errors.phone.countryCode`, etc.
You need to modify your logic for this. Something like the following:
<script setup>
// ...
const phoneErrors = computed(() => {
const phoneProps = ['e164', 'countryCallingCode', 'countryCode', ...etc];
return phoneProps .reduce((prev, curr) => {
if(form.errors.phone && form.errors.phone[curr])
prev.push(form.errors.phone[curr][0]);
return prev;
}, []);
});
// ...
</script>
<template>
<!-- .... -->
<!-- Displaying phone errors-->
<div v-for="error in phoneErrors">{{ error }}</div>
<!-- .... -->
</template>
Please or to participate in this conversation.