0
votes

I'm trying to submit a form request using axios, have it validated, and return errors if the validation fails. The problem is, when I submit the form, no error messages are returned for me to show on the client side. Here's the HTTP request and vue component:

         <div class="card">
            <div class="card-header">
                    <h4>Information</h4>
            </div>

        <div class="card-body">
            <p v-if='!isEditMode'><strong>Name:</strong> {{businessData.name}}</p> 
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-name">Name</label></strong> 
                <input class="form-control" name="business-name"  v-model='businessData.name'>
            </div>
            <p v-if='!isEditMode'><strong>Description:</strong> {{businessData.description}}</p> 
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-description">Description</label></strong> 
                <textarea class="form-control normal" name="business-description" 
                    placeholder="Enter your services, what you sell, and why your business is awesome" 
                    v-model='businessData.description'></textarea>
            </div>
        </div>

    </div>

    <div class="card">
        <h4 class="card-header">Address Information</h4>
        <div class="card-body">

            <p v-if="!isEditMode"><strong>Street Address: </strong> {{businessData.street}}</p>
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-street">Street Address: </label></strong> 
                <input type="text" class="form-control" name="business-street"  v-model='businessData.street' placeholder="1404 e. Local Food Ave">
            </div>
            <p v-if="!isEditMode"><strong>City: </strong> {{businessData.city}}</p>
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-city">City: </label></strong> 
                <input class="form-control" type="text" name="business-city"  v-model='businessData.city'>
            </div>
            <p v-if="!isEditMode"><strong>State: </strong> {{businessData.state}}</p>
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-state">State: </label></strong> 
                <select class="form-control" name="business-state"  id="state" v-model="businessData.state" >...</select>                                 
            </div>
            <p v-if="!isEditMode"><strong>Zip: </strong> {{businessData.zip}}</p>
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-zip">Zip: </label></strong> 
                <input class="form-control" type="text" maxlength="5" name="business-zip"  v-model='businessData.zip'>
            </div>

        </div>
    </div>
    <div class="card">
        <h4 class="card-header">Contact Information</h4>
        <div class="card-body">
            <p v-if="!isEditMode"><strong>Phone: </strong> {{businessData.phone}}</p>
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-phone">Phone: </label></strong> 
                <input class="form-control" type="tel" name="business-phone"  v-model='businessData.phone'>
            </div>
            <p v-if="!isEditMode"><strong>Email: </strong> {{businessData.email}}</p>
            <div class="form-group" v-if='isEditMode'>
                <strong><label for="business-Email">Email: </label></strong> 
                <input class="form-control" type="email" name="business-email"  v-model='businessData.email'>
            </div>
        </div>
    </div>
</div>
<script>
export default {
    data () {
        return {
            isEditMode: false,
            businessData: this.business,
            userData: this.user,
            errors: []
        }
    },
    props: {
        business: {},
        user: {},
        role: {}
    },
    //Todo - Institute client side validation that prevents submission of faulty data
    methods: {
        validateData(data) {

        },
        saveBusinessEdits () {
            axios.put('/businesses/' + this.business.id , {updates: this.businessData})
            .then(response => {
                console.log(response.data)
                // this.businessData = response.data;
                this.isEditMode = false;
            })
            .catch (response => {
                console.log(response.data)
                this.isEditMode = false;
            })
        },
        saveUserEdits () {
            axios.put('/profile/' + this.user.id , {updates: this.userData})
            .then(response => {
                console.log(response.data)
                this.userData = response.data;
                this.isEditMode = false;
            })
            .catch (response => {
                console.log(response)
                this.isEditMode = false;
            })
        }
    }
}

Route

Route::put('/businesses/{id}', 'BusinessesController@update');

BusinessController and update function

  public function update(BusinessRequest $request, $id)
{

    $business = Business::find($id)->update($request->updates);


    $coordinates = GoogleMaps::geocodeAddress($business->street,$business->city,$business->state,$business->zip);
        if ($coordinates['lat']) {
            $business['latitude'] = $coordinates['lat'];
            $business['longitude'] = $coordinates['lng'];
            $business->save();
            return response()->json($business,200);
        } else {                    
            return response()->json('invalid_address',406);
        }
        $business->save();
        return response()->json($business,200);
}

and BusinessRequest class

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'business-name'=> 'required|string|max:255',
        'business-description'=> 'required|string',
        'business-phone' => 'nullable|phone|numeric',
        'business-email' => 'nullable|email',
        'business-street'=> 'required|string',
        'business-city' => 'required|string',
        'business-state' => 'required|string|max:2',
        'business-zip' => 'required|min:5|max:5|numeric', 
    ];
}

public function messages() {
    return [
        'business-zip.min:5' =>'your zip code must be a 5 characters long',
        'business-email.email'=>'your email is invalid',
        'business-phone.numeric'=>'your phone number is invalid',
    ];
}
}

I don't understand why, even if input valid data, it responds with a 422 response and absolutely no error messages. Since this is laravel 5.6, the 'web' middleware is automatic in all of the routes in the web.php file. So this isn't the problem. Could anyone help me normalize the validation behavior?

2
What, if anything, is returned in the response?Kingsley
This is what's logged to the console: Error: Request failed with status code 422 at createError (app.js:13822) at settle (app.js:35256) at XMLHttpRequest.handleLoad (app.js:13696)Adam French
In the browser you're using, look for the Network tab (where it shows the site's network connections for downloading css, js, etc.), in there, you'll see your ajax request. There should be a response tab, where you can see what the server responded to your request with. That should have some more information about why it failed. Here's Chrome: imgur.com/a/0kdxdAaKingsley
Did one of the below answers solve your issue?Rwd

2 Answers

6
votes

In Laravel a 422 status code means that the form validation has failed.

With axios, the objects that are passed to the then and catch methods are actually different. To see the response of the error you would actually need to have something like:

.catch (error => {
    console.log(error.response)
    this.isEditMode = false;
})

And then to get the errors (depending on your Laravel version) you would have something like:

console.log(error.response.data.errors)

Going forward it might be worth having a look at Spatie's form-backend-validation package

0
votes

You can use Vue.js and axios to validate and display the errors. Have a route called /validate-data in a controller to validate the data.

app.js file:

       import Vue           from 'vue'
        window.Vue = require('vue');
        window.axios = require('axios');

        window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

        let token = document.head.querySelector('meta[name="csrf-token"]');

        if (token) {
            window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
        } else {
            console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
        }

             class Errors {
                constructor() {
                    this.errors = {};
                }

                get(field) {
                    if (this.errors[field]) {

                        return this.errors[field][0];
                    }
                }

                record(errors) {
                    this.errors = errors;
                }

                clear(field) {
                    delete this.errors[field];
                }

                has(field) {
                    return this.errors.hasOwnProperty(field);
                }

                any() {
                    return Object.keys(this.errors).length > 0;
                }
            }

            new Vue({
             el: '#app',

                data:{
               errors: new Errors(),
              model: {
    business-name: '',
    business-description: '',
    business-phone: ''
    },
            },

        methods: {
          onComplete: function(){
              axios.post('/validate-data', this.$data.model)
                  // .then(this.onSuccess)
                  .catch(error => this.errors.record(error.response.data.errors));
           },
}
        });

Make a route called /validate-data with a method in the controller, do a standard validate

$this->validate(request(), [
 'business-name'=> 'required|string|max:255',
    'business-description'=> 'required|string',
    'business-phone' => 'nullable|phone|numeric',
    'business-email' => 'nullable|email',
    'business-street'=> 'required|string',
    'business-city' => 'required|string',
    'business-state' => 'required|string|max:2',
    'business-zip' => 'required|min:5|max:5|numeric'
]

);

Then create your inputs in your view file, using v-model that corresponds to the vue.js data model fields. Underneath it, add a span with an error class (basic red error styling, for example) that only shows up if the errors exist. For example:

<input type="text" name="business-name" v-model="model.business-name" class="input">
<span class="error-text" v-if="errors.has('business-name')" v-text="errors.get('business-name')"></span>

Don't forget to include the app.js file in footer of your view file. Remember to include the tag, and run npm run watch to compile the vue code. This will allow you to validate all errors underneath their input fields.

Forgot to add, have a buttton that has @onclick="onComplete" to run the validate method.