4

I am new in livewire. i am working on registration process of user. Register form is quite big and validations also working fine to me. The problem is when user submits the form validations came but user not able to see because submit button is at bottom and form is at top.

When i manually scroll to top error message is displaying. So what i want when user submits the form its will automatically go to first error message. Here is my code:

<form wire:submit.prevent="userRegister">
<div class="row">
    <div class="col-md-3">
        <label>First Name *</label>
    </div>
    <div class="col-md-9">
        <input type="text" wire:model="register.first_name" placeholder="Enter First Name" required>
        @error('register.first_name') <span class="text-danger">{{ $message }}</span> @enderror
    </div>
</div>
<div class="row">
    <div class="col-md-3">
        <label>Last Name *</label>
    </div>
    <div class="col-md-9">
        <input type="text" wire:model="register.last_name" placeholder="Enter Last Name">
        @error('register.last_name') <span class="text-danger">{{ $message }}</span> @enderror
    </div>
</div>
<div class="row">
    <div class="col-md-3">
        <label>Test 1 Name *</label>
    </div>
    <div class="col-md-9">
        <input type="text" wire:model="register.test1_name" placeholder="Enter Last Name">
    </div>
</div>
<div class="row">
    <div class="col-md-3">
        <label>Test 2 Name *</label>
    </div>
    <div class="col-md-9">
        <input type="text" wire:model="register.test2_name" placeholder="Enter Last Name">
    </div>
</div>
<div class="row">
    <div class="col-md-3">
        <label>Test 3 Name *</label>
    </div>
    <div class="col-md-9">
        <input type="text" wire:model="register.test3_name" placeholder="Enter Last Name">
    </div>
</div>
<div class="row">
    <div class="col-md-3"></div>
    <div class="col-md-9">
        <!--<input type="submit" value="Edit" class="edt-sb">-->
        <input wire:click="userRegister" type="submit" value="Submit" class="edt-sv">
    </div>
</div>
</form>

My Register.php

<?php

namespace App\Http\Livewire;

use Livewire\Component;
class Register extends Component
{
public $register;
protected $rules = [
    'register.first_name' => 'bail|required|max:50',
    'register.last_name' => 'bail|required|max:50',
];

protected $messages = [
    'register.first_name.required' => 'Please enter first name',
];

public function userRegister(){
    $this->validate();
}

I want when user submits the form it will immediately scroll to first error message. Currently my validations work perfects for me. Do i need to use alpine js? for this.

James Z
  • 12,209
  • 10
  • 24
  • 44
kunal
  • 4,122
  • 12
  • 40
  • 75

3 Answers3

0

Livewire errors will be saved in $errors bag so probably you can add an id to each field in your form and use alpinejs to focus the first element with the id present in the errors bag, something like:

        <div x-data="{
                'errors': {{ json_encode(array_keys($errors->getMessages())) }},
                focusField(input) {
                    fieldError = document.getElementById(input);
                    if (fieldError) {
                        fieldError.focus({preventScroll:false});
                    }
                },
            }"
             x-init="() => { $watch('errors', value => focusField(value[0])) }">
            
                    <input id="register.test3_name" type="text" wire:model="register.test3_name" placeholder="Enter Last Name">
        </div>
0

I nearly faced the same issue but with success message not error messages, for error messages I used updated(), they are shown on real time for user while filling the fields.

My Class Component :

class FormSubmissions extends Component
{
    public $city;
    public $fullname;

    protected $rules = [
        'city' => 'required',
        'fullname' => 'required',
    ];

    public $successMessage;

    public function updated($propertyName)
    {
        $this->validateOnly($propertyName);
    }

    public function submitForm()
    {
        $submission = $this->validate();



        $this->successMessage = trans('site.success');


        $this->resetForm();
    }


    public function resetForm()
    {
        $this->city = '';
        $this->fullname = '';
    }

}

My Blade template :

<div id="formSection" class="form-wrapper">
    @if ($successMessage)
        <div  class="alert alert-success alert-dismissible fade show" role="alert">
            {{ $successMessage }}
            <button  wire:click="$set('successMessage', null)" type="button" class="close" data-dismiss="alert" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
        </div>
        <script>
            var elmnt = document.getElementById("formSection");
            elmnt.scrollIntoView();
        </script>
    @endif
    <form wire:submit.prevent="submitForm" action="/" method="POST">
    ...

</div>

So the small script added with success message allowed me to scroll back to top of my form Section #formSection.

Dharman
  • 30,962
  • 25
  • 85
  • 135
RedaMakhchan
  • 482
  • 3
  • 9
  • what if i have big form and divides in to 3 parts and error message is in middle of the forn? – kunal Mar 01 '21 at 11:10
  • In this case the selector (Id of field for example) need to be dynamic, so you can scroll to it depending on which field has error. In my code FormSection will be defined from Controller, I did find a related issue opened on live wire here : https://github.com/livewire/livewire/issues/1415 – RedaMakhchan Mar 06 '21 at 12:32
0

If you are using <x-jet-input-error/> component for displaying errors, you can just modify resources/views/vendor/jetstream/components/input-error.blade.php (make sure you have published livewire views) file to below code:

@props(['for'])

<span x-data="{hasError: '{{$errors->get($for)[0] ?? ''}}' }"
     x-init="()=> { $watch('hasError', (value)=> {
            let errorDiv = document.getElementsByClassName('invalid-feedback')[0];
            if(errorDiv){
                errorDiv.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'nearest'});
            }
    })}">
    @error($for)
        <span {{ $attributes->merge(['class' => 'invalid-feedback d-block']) }} role="alert" style="font-size: inherit">
            <strong>{{ $message }}</strong>
        </span>
    @enderror
</span>

Explanation : we use alpine to watch for changes in the validation message for the "$for" field. As soon as it changes (validation message shows up or vanishes), it looks for the error block and scrolls to it

Avinash
  • 56
  • 3