Handling Laravel Validation Error Messages With Vue.js

Display Laravel validation error messages with Vue.js

In this post, I will be showing you how to handle Laravel validation error messages with Vue.js.

Recently, I launched a open source side project I was working on called Open Laravel. Open Laravel allows developers to submit open source projects that were built using the Laravel framework. The project submission page uses Vue.js to submit the form asynchronously through an AJAX request. Since I am no longer sending the form the default way (refreshing after form submission), I needed a way to show Laravel form validation error messages. After going back and forth with some forum questions and answers, I was able to make it work.

How I solved this is what I'll be showing you in this tutorial. A quick demo is shown below.

Let's Begin

I'll be using a basic post creation demo for this tutorial. I'll start with a fresh installation of Laravel. I named the demo vuejs-laravel-validation-error-messages, feel free to name it whatever you like.

laravel new vuejs-laravel-validation-error-messages

For this tutorial, we'll be making use of Laravel Elixir. Laravel Elixir is tool that allows you perform Gulp tasks inside your Laravel applications.

More on it can be found on Laravel Elixir. We'll be using Laravel Elixir to build and compile the JavaScript we will be writing. So before anything, we need to install Laravel Elixir dependencies:

npm install

Project Dependencies

Having installed Laravel Elixir dependencies, it time to define and install the project dependencies. We'll be making use of the following:

Obviously we need Vue.js as indicated in the tutorial title. vue-resource which is an HTTP client for Vue.js making web requests and handled responses using XMLHttpRequest or JSONP. And lastly laravel-elixir-vueify is a wrapper for Laravel Elixir and the Browserify Vueify plugin which allows you to write your templates, scripts and styles all in one .vue file. Install each of the dependencies using npm. With the project dependencies installed, we can get to the meat of the project.

The Routes

Open routes.php and paste the code below into it

// routes.php

Route::post('create-post', 'PostsController@save');

Posts Controller

Create a PostsController that will handle the logic for our post creation demo.

php artisan make:controller PostsController

Add a save() to the PostsController

// PostsController.php

public function save(Request $request)
{
    // set form validation rules
    $this->validate($request, [
        'title' => 'required',
        'body'  => 'required'
    ]);

    // if the validation passes, save to database and redirect
}

The save() handles post creation form validation.

For the purpose of the tutorial, I'm keeping the validation simple by only making both the title and body fields required.

Post View

Create a new file inside the views directory and name it post.blade.php. Paste the code below in it.

<!DOCTYPE html>
<html>
    <head>
    // add csrf token
    <meta id="token" name="token" value="{{ csrf_token() }}">

    <title>Handling Laravel Validation Error Messages With Vue.js</title>

        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

        <link href="https://fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css">

        <style>
            body {
                padding: 50px;
            }    
        </style>
    </head>
    <body>
        <div class="container">
            // this will be displayed upon successful submission of form
            <div class="alert alert-success" v-if="submitted">
                Post created!
            </div>

            //  prevent the page from refreshing after submission
            <form @submit.prevent="createPost" method="POST">
                <legend>Create Post</legend>                

                // add Bootstrap .has-error if title field has errors
                <div class="form-group@{{ errors.title ? ' has-error' : '' }}">
                    <label>Post Title</label>
                    <input type="text" name="title" class="form-control" v-model="post.title" value="{{ old('title') }}">

                    // display errors if field has errors using FormError component
                    <form-error v-if="errors.title" :errors="errors">
                        @{{ errors.title }}
                    </form-error>
                </div>

                // add Bootstrap .has-error if body field has errors
                <div class="form-group@{{ errors.body ? ' has-error' : '' }}">
                    <label>Post Body</label>
                    <textarea name="body" class="form-control" rows="5" v-model="post.body">{{ old('body') }}</textarea>

                     // display errors if field has errors using FormError component
                     <form-error v-if="errors.body" :errors="errors">
                        @{{ errors.body }}
                    </form-error>
                </div>

                <button type="submit" class="btn btn-primary">Create Post</button>
            </form>
        </div>
    <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

You should have a form similar to the image below:

Again, a simple form. There is a success message that will be displayed upon submission of the form using v-if.

Since we want the form to be submitted asynchronously (without page refresh), @submit.prevent will prevent the page from refreshing after submission.

The form input data will be POSTed to a createPost function which we are yet to create. As you can see, I'm doing {{ errors.title ? ' has-error' : '' }} which will add Bootstrap .has-error if the title field has errors. Same applies to the body field.

To display the error messages when validation fails, I'm using the FormError component (which we are yet to create) and passing to it errors (which contains error messages) as props.

Vue.js File

Create a new js folder inside resources/assests directory. Inside the newly created js folder, create an app.js file. Before going further, open the gulpfile.js file and update it with:

// gulpfile.js

var elixir = require('laravel-elixir');

require('laravel-elixir-vueify');

elixir(function(mix) {
    mix.browserify('app.js');
});

Notice above, where we require laravel-elixir-vueify, which will compile our .vue file when we run gulp.

In the resources/assests/js/app.js file, paste the code below into it.

// resources/assests/js/app.js

import Vue from 'vue';
import VueResource from 'vue-resource';

// tell Vue to use the vue-resource plugin
Vue.use(VueResource);

// import FormError component
import FormError from './components/FormError.vue';

// get csrf token
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('value');

// instantiate a new Vue instance
new Vue({
    // mount Vue to .container
    el: '.container',

   // define components
    components: {
        FormError,
    },

    data: {
        post: {
            title: '',
            body: '',
        },

        submitted: false,

        // array to hold form errors
        errors: [],
    },

    methods: {
        createPost() {
            let post = this.post;

            this.$http.post('create-post', post).then(function(response) {
                // form submission successful, reset post data and set submitted to true
                this.post = {
                    title: '',
                    body: '',
                };

                // clear previous form errors
                this.$set('errors', '');

                this.submitted = true;
            }, function (response) {
                // form submission failed, pass form  errors to errors array
                this.$set('errors', response.data);
            });
        }
    }
});

First, we import the packages (vue.js and vue-resource) installed earlier and tell Vue.js to use the vue-resource plugin. Also, we import FormError component which we'll create shortly.

Next we create a Vue instance and pass it an options object. The el tells Vue.js the element to mount on. The components object accepts any components (FormError in our case) we want included in our Vue instance. The data object contain data that can be rendered in a view. The post object contains a title and body object. It will hold the created post and will be binded to the post creation input fields. The submitted defaults to false will be used to show the success message upon submission of form. The errors array will obviously hold the errors messages returned during form validation. Finally the methods object contains a single createPost().

Taking a closer look at the createPost(), first we declare a variable post to hold the post data (submitted form data since we have two-way binding). Next, we send a post request to the route create-post using the post() of vue-resource plugin passing along the post variable (which now holds the submitted form data). If the request was successful, we set the form input value to empty string and set submitted to true to show the success message. But if the request was not successful (that is the form validation failed), we set the errors array to hold the form validation error messages Laravel sends upon failed validation.

FormError Component

The FormError component is going to be a very simple component. Create a new components folder in resources/assests/js and create a new file FormError.vue inside the newly created components folder. Paste the snippets below into it

<template>
    <span class="help-block">
        // this will be replaced by the error messages
        <slot></slot>
    </span>
</template>

<script>
    export default {
        props: ['errors'],
    }
</script>

The template is a simple Bootstrap help block. The <slot> tag will allow us to pass dynamic content into the template. In our case the <slot> will be replaced by the error messages. Since the form validation error messages are unique to each form field, having the template show a specific error message for each field makes sense. The FormError component accepts a props errors which must be passed along to the template.

Now if you submit the form without filling the appropriate fields, you will get something similar to the image below showing the error messages:

But if you fill the form as appropriate, you should get something like below:

Conclusion

That's it. I hope you find this tutorial useful. If you encounter any problems following this tutorial or have suggestions, kindly drop them in the comment below.

Chimezie Enyinnaya

web developer [PHP Laravel VueJS|AngularJS] | movie lover | I run http://openlaravel.com | blogs at http://itoocode.com