Backend Development

Handling Forms And Validations With Laravel Inertiajs

In this post we will learn how to submit forms and work with validations when working with Inertia-js library and Laraval.

 

 

If you have not used Inertia-js before, inertia is a library for building monolithic apps without creating an Api thereby reducing efforts regrading Api creation. When used beside laravel it acts as a bridge between the frontend App which can be (React or Vue) and the Server side App (Laravel).

In this post examples we are using Vue 3 as a frontend with Laravel. 

For the sake of simplicity i suppose we already have a laravel app with inertia installed, and we have simple PostsController with this code:

app/Http/PostsController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;

class PostsController extends Controller
{
    public function create()
    {
        return Inertia::render('create-post');
    }

    public function store(Request $request) 
    {
         // 
    }
}

The create() method renders the Vue page “create-post.vue”. let’s see the code for create-post.vue

resources/js/Pages/create-post.vue

<script setup>
    import {reactive} from 'vue';

    const form = reactive({
        title: '',
        description: '',
        author: ''
    })

    function save() {
        console.log(form);
    }
</script>

<template>
    <form method="post" @submit.prevent="save" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
        <div class="mb-4">
            <label class="block text-gray-700 text-sm font-bold mb-2">Title</label>
            <input type="text" name="title" v-model="form.title" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" />
        </div>
        <div class="mb-4">
            <label class="block text-gray-700 text-sm font-bold mb-2">Description</label>
            <textarea name="description" v-model="form.description" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700"></textarea>
        </div>
        <div class="mb-4">
            <label class="block text-gray-700 text-sm font-bold mb-2">Author</label>
            <input type="text" name="author" v-model="form.author" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" />
        </div>
        <div>
            <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">Save</button>
        </div>
    </form>
</template>

In this component i am using Vue 3 composition Api by utilizing the <script setup>. In the template i created a simple form to add a post styled using tailwindcss classes.

In the script the form is bound to a reactive object using vue 3 reactive function so that we can use it with v-model directive.

routes/web.php

Route::get('posts/create', [\App\Http\Controllers\PostsController::class, 'create']);
Route::post('posts', [\App\Http\Controllers\PostsController::class, 'store']);

Now when you navigate to the url http://<host>/posts/create , you will see the form.

Create a migration for posts table:

php artisan make:Model Post -m

This command creates a Post model and -m flag creates the associated migration.

The posts migration file:

Open database/migrations/xxxx_xx_xx_create_posts_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description');
            $table->string('author');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Update the database settings .env then run migrate

php artisan migrate

 

Submitting The Form

Inertia provide us the router module which enable us to submit forms. The inertia router contains various methods to handle CRUD operations like post, put, delete, update:

router.post('/posts', data);
router.put('/posts', data);
router.patch('/posts', data);
router.get('/posts/1');
router.delete('/posts/1');

 

To start submitting the form first import the inertia router for inertia vue 3 like so:

import { reactive } from 'vue'
import { router } from '@inertiajs/vue3'

Then inside of the save() function we call router.post()

 function save() {
        router.post('/posts', form);
}

The router.post() function accepts the route url as the first argument which already defined in web.php and payload as the second argument.

Let’s update the PostsController::store() method to capture the request data, validate and save.

import the Post model at the top:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use App\Models\Post;

...
...
public function store(Request $request)
    {
        $this->validate($request, [
           'title' => 'bail|required|string|min:4',
           'description' => 'bail|required|string',
           'author' => 'bail|required|string|min:3'
        ]);

        $post = new Post();
        $post->title = $request->title;
        $post->description = $request->description;
        $post->author = $request->author;
        $post->save();

        return redirect()->back()->with('success', 'Post created successfully');
    }

I validated the incoming data using the usual laravel way. In normal frontend apps sometimes we have to return a JSON response as we were dealing with ajax XHR requests, but inertia help us remove this burden.

Next i saved the data in the eloquent model Post and i redirect back with a success message.

 

Displaying validation errors

At this point let’s display the errors on the Vue form. To accomplish Inertia uses special prop called “errors” available in page.props.errors object, so all we have to do is to declare this prop and check for the existence any related property in the validation bag.

In Pages/create-post.vue add this line:

defineProps({ errors: Object });

After import { router } from ‘@inertiajs/vue3’

Now we have errors prop in place let’s update the form and add a div below each input to check for any validation property in errors like so:

<form method="post" @submit.prevent="save" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
        <div class="mb-4">
            <label class="block text-gray-700 text-sm font-bold mb-2">Title</label>
            <input type="text" name="title" v-model="form.title" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" />
            <div v-if="errors.title" class="text-red-600 mt-2 text-sm">{{ errors.title }}</div>
        </div>
        <div class="mb-4">
            <label class="block text-gray-700 text-sm font-bold mb-2">Description</label>
            <textarea name="description" v-model="form.description" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700"></textarea>
            <div v-if="errors.description" class="text-red-600 mt-2 text-sm">{{ errors.description }}</div>
        </div>
        <div class="mb-4">
            <label class="block text-gray-700 text-sm font-bold mb-2">Author</label>
            <input type="text" name="author" v-model="form.author" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" />
            <div v-if="errors.author" class="text-red-600 mt-2 text-sm">{{ errors.author }}</div>
        </div>
        <div>
            <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">Save</button>
        </div>
    </form>

As shown the three div’s to check for errors like below:

<div v-if="errors.title" class="text-red-600 mt-2 text-sm">{{ errors.title }}</div>

Check the form now and click submit you will notice the errors appears in case of invalid or required data.

 

Displaying success message and reset the form

In the the above Controller in the store() method after saving the data we redirect back with a flash message. We need to display this message in the top of the form. Inertia enable us to do so using shared data.

Open app/Http/Middleware/HandleInertiaRequests.php

public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'flash' => [
                'success' => fn () => $request->session()->get('success')
            ],
        ]);
    }

The share() method shares data across multiple pages. For flash messages add all the flash keys your application using under the ‘flash‘ key. In this case we have one key ‘success’ that is returned in the store() method.

You might have key for error and key for warning:

return array_merge(parent::share($request), [
            'flash' => [
                'success' => fn () => $request->session()->get('success'),
                'error' => fn () => $request->session()->get('error'),
                'warning' => fn () => $request->session()->get('warning')
            ],
        ]);

After that display this flash message on the Vue app by checking for $page.props.flash.success

In Pages/create-post.vue add this div before the <form /> tag

<div class="bg-green-400 p-3" v-if="$page.props.flash.success">
        {{ $page.props.flash.success }}
    </div>

The flash message is showing now. The second step is to reset the form. Here in inertia we need a way to tell if the form submission is successful.

This can be done using some events given by Inertia provided in the router module:

Add this code after the save() function:

router.on('success', (event) => {
        form.title = '';
        form.description = '';
        form.author = '';
    });

There is also other events like:

router.on('error', (errors) => {

})
router.on('invalid', (event) => {
  console.log(`An invalid Inertia response was received.`)

})
router.on('exception', (event) => {
  console.log(`An unexpected error occurred during an Inertia visit.`)
})

Code Repository

 

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments