Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

olimorris's avatar

[L5.1 and vue.js] Error handing with Laravel and vue.js 1.0

Hi all,

I'm building an app which uses vue.js and Laravel 5 to persist data into a database. I've used Laravel Spark as a starting point (my app requires a little more customisation than Spark can offer at the minute) and can successfully save data.

However, I'm having trouble with displaying errors when the user validation fails using the 'out of the box' authorisation that Laravel has. I keep getting the error:

[Vue warn]: Error when evaluating expression "form.errors.length > 0" (referring to errors.js in my code below)

Piecing it together

So how this should work...a user enters their details into the registration page and Vue (using vue-resource) makes an AJAX request to the 'out of the box' Laravel AuthController@postRegister method. On error, Laravel nicely spits out the JSON error messages we all expect. Now in theory, the sendRegistration method within my subscription.js file should detect the errors that Laravel spits out (I've tested this with console.log's and it works) and pass them into the errors.js component to display the errors within the <spark-errors> tags. In order to do this, it uses a setErrorsOnForm: function within my <head> tag. However it's not working as expected and I cannot workout why.

My Code

My code consists of a registration page:

<spark-subscription-register-screen inline-template>
<div class="panel panel-default">
    <div class="panel-heading">Your Information</div>
    <div class="panel-body">
        <spark-errors form="@'{'{ registerForm '}'}"></spark-errors>

        <form class="form-horizontal" role="form" id="subscription-basics-form">

            <div class="form-group">
                <label class="col-md-4 control-label">Your Name</label>
                <div class="col-md-6">
                    <input type="text" class="form-control spark-first-field" name="name" v-model="registerForm.name">
                </div>
            </div>

            <div class="form-group">
                <label class="col-md-4 control-label">E-Mail Address</label>
                <div class="col-md-6">
                    <input type="email" class="form-control" name="email" v-model="registerForm.email">
                </div>
            </div>

            <div class="form-group">
                <label class="col-md-4 control-label">Password</label>
                <div class="col-md-6">
                    <input type="password" class="form-control" name="password" v-model="registerForm.password">
                </div>
            </div>

            <div class="form-group">
                <label class="col-md-4 control-label">Confirm Password</label>
                <div class="col-md-6">
                    <input type="password" class="form-control" name="password_confirmation" v-model="registerForm.password_confirmation">
                </div>
            </div>

            <div v-if="freePlanIsSelected">
                <div class="form-group">
                    <div class="col-sm-6 col-sm-offset-4">
                        <div class="checkbox">
                            <label>
                                <input type="checkbox" v-model="registerForm.terms"> I Accept The <a href="/terms" target="_blank">Terms Of Service</a>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-sm-6 col-sm-offset-4">
                        <button type="submit" class="btn btn-primary" v-on:click="register" :disabled="registerForm.registering">
                            <span v-if="registerForm.registering">
                                <i class="fa fa-btn fa-spinner fa-spin"></i> Registering
                            </span>

                            <span v-if=" ! registerForm.registering">
                                <i class="fa fa-btn fa-check-circle"></i> Register
                            </span>
                        </button>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>
</spark-subscription-register-screen inline-template>

From a vue.js perspective there is a subscription.js file:

Vue.component('spark-subscription-register-screen', {

    /*
     * Initial state of the component's data.
     */
    data: function () {
        return {
            registerForm: {
                nhs_org: '', team_name: '', name: '', email: '', password: '', password_confirmation: '',
                plan: '', terms: false, coupon: null, invitation: null,
                stripe_token: null, errors: [], registering: false
            },
        };
    },

    methods: {
        /*
         * Initialize the registration process.
         */
        register: function(e) {
            var self = this;

            e.preventDefault();

            this.registerForm.errors = [];
            this.registerForm.registering = true;

            return this.sendRegistration();
        },

        /*
         * After obtaining the Stripe token, send the registration to Spark.
         */
        sendRegistration: function() {
            this.$http.post('/register', this.registerForm)
                .success(function(response) {
                    window.location = '/';
                })
                .error(function(errors) {
                    this.registerForm.registering = false;
                    Spark.setErrorsOnForm(this.registerForm, errors);
                });
        },
    }

});

and there is an errors.js Vue component:

/*
 * Common Error Display Component.
 */
Vue.component('spark-errors', {
    props: ['form'],

    template: "<div><div class='alert alert-danger' v-if='form.errors.length > 0'>\
                <strong>Whoops!</strong> There were some problems with your input.<br><br>\
                <ul>\
                    <li v-for='error in form.errors'>\
                        {{ error }}\
                    </li>\
                </ul>\
            </div></div>"
});

as well as a global script which I have included into the <head> tag of all my views:

<!-- Laravel Spark Globals -->
<script>
    window.Spark = {
        // Laravel CSRF Token
        csrfToken: '{{ csrf_token() }}',

        // Current User ID
        userId: {!! Auth::user() ? Auth::id() : 'null' !!},

        // Current Team ID
        @if (Auth::user() && Spark::usingTeams() && Auth::user()->hasTeams())
            currentTeamId: {{ Auth::user()->currentTeam->id }},
        @else
            currentTeamId: null,
        @endif

        // Flatten errors and set them on the given form
        setErrorsOnForm: function (form, errors) {
            if (typeof errors === 'object') {
                form.errors = _.flatten(_.toArray(errors));
            } else {
                form.errors.push('Something went wrong. Please try again.');
            }
        }
    }
</script>

Now the Vue files which piece all of this together (in the correct order):

app.js:

require('./core/dependencies');

if ($('#spark-app').length > 0) {
    new Vue(require('./core/spark.js'));
}

core/dependencies.js:

/*
 * Load Vue & Vue-Resource.
 *
 */
if (window.Vue === undefined) window.Vue = require('vue');

require('vue-resource');
Vue.http.headers.common['X-CSRF-TOKEN'] = Spark.csrfToken;

/*
 * Load Underscore.js, used for map / reduce on arrays.
 */
if (window._ === undefined) window._ = require('underscore');

/*
 * Load jQuery and Bootstrap jQuery, used for front-end interaction.
 */
if (window.$ === undefined || window.jQuery === undefined) window.$ = window.jQuery = require('jquery');
require('bootstrap-sass/assets/javascripts/bootstrap');

core/spark.js:

/*
 * Load the Spark components.
 */
require('./components');

/**
 * Export the Spark application.
 */
module.exports = {
    el: '#spark-app',

    /*
     * Bootstrap the application. Load the initial data.
     */
    ready: function () {
        $(function() {
            $('.spark-first-field').filter(':visible:first').focus();
        });
    }
}

and core/components.js:

require('./../auth/registration/subscription');
require('./../common/errors');
0 likes
7 replies
uxweb's avatar

Hi @olimorris Where is the template for the cardForm?

Also, change this markup:

<spark-errors form="@"></spark-errors>

for this:

<spark-errors form="registerForm"></spark-errors>

// here the "form" attribute is a Vue.js prop for parent-child component communication :)

Spark is passing the main register form to a component called spark-errors, this component immediately shows any errors set on the registration form through a prop.

olimorris's avatar

Hi @uxweb thanks for your help.

Good challenge on the cardForm. I've just removed it from my code as we speak.

Also, i've just noticed the spark-errors tag should have been <spark-errors form="@`{`{ registerForm `}`}"></spark-errors> (ignore the ` as the Laracasts syntax highlighter was preventing it from displaying correctly).

Having tried your answer above, it's still not working and isn't this an example of parent-child component communication?

uxweb's avatar

@olimorris Yes, it is in here:

http://vuejs.org/guide/components.html#Props

It should work like this for passing the registerForm object as Vue.js 1.0:

<spark-errors v-bind:form="registerForm"></spark-errors>

// or with the shorthand:

<spark-errors :form="registerForm"></spark-errors>

I saw a tweet today from @TaylorOtwell saying that he's updating spark to Vue.js 1.0. :)

vitorarjol's avatar
Level 10

@olimorris The problem is the

<spark-errors form="@'{'{ registerForm '}'}"></spark-errors>

In the 1.0.* of Vue Evan has changed the bindings a little bit.

Try using :form to bind the form properly, like this:

<spark-errors :form="@'{'{ registerForm '}'}"></spark-errors>

Just a note here, I don't know if is a typo or custom tags, but I just use @{{ }}, without the '. I just reproduced your code, so if it doesn't work try to remove the quotes :D

I've struggled with this just yesterday haha.

Reference:

<!-- props -->
<my-comp
  prop="literal string"
  v-bind:prop="defaultOneWay"
  v-bind:prop.sync="twoWay"
  v-bind:prop.once="oneTime">
</my-comp>

<!-- component with props, in shorthand -->
<my-comp
   prop="literal string"
  :prop="defaultOneWay"
  :prop.sync="twoWay"
  :prop.once="oneTime">
</my-comp>

https://github.com/vuejs/vue/wiki/1.0.0-binding-syntax-cheatsheet

Please or to participate in this conversation.