0

I have a form (http://element.eleme.io/#/en-US/component/form) in application, where I do server side validation. But I have not glu how to add error message for specific inputs.

4 Answers 4

1

Each el-form-item needs a prop attribute for the frontend validation to work. They also need a bound error attribute. I just made each the same as the field name for simplicity, like so:

<el-form-item label="Email" prop="email" :error="errors.email">
    <el-input v-model="form.email" type="email"></el-input>
</el-form-item>

Then when the form is submitted I run my validator for the frontend rules (using Element's rules). Right after that I use axios to post to the server (Laravel). I loop through any errors, and update the value in the errors object. Whenever the form is submitted, I clear the errors (if you don't clear them, the errors will not show up on consecutive form submissions).

data() {

    let passwordsMatch = (rule, value, callback) => {
        if ( value != this.form.password )
            return callback(new Error('Passwords do not match'));

        return callback();
    };

    let form = {
        first_name: '',
        last_name: '',
        email: '',
        phone: '',
        password: '',
        password_confirmation: '',
    };

    // copy blank values, not reference
    let errors = {...form};
    let blankErrors = {...form};

    return {

        form,

        errors,

        blankErrors,

        rules: {
            first_name: [
                { required: true, message: 'First Name is required', trigger: 'blur' },
            ],
            last_name: [
                { required: true, message: 'Last Name is required', trigger: 'blur' },
            ],
            email: [
                { required: true, message: 'Email is required', trigger: 'blur' },
                { type: 'email', message: 'Must be an email', trigger: 'blur' },
            ],
            phone: [
                { required: true, message: 'Cell Phone is required', trigger: 'blur' },
                // TODO: finish phone validation
                //{ type: 'number', message: 'Must be a phone number', trigger: 'blur' },
            ],
            password: [
                { required: true, message: 'Password is required', trigger: 'blur' },
            ],
            password_confirmation: [
                { required: true, message: 'Password is required', trigger: 'blur' },
                { validator: passwordsMatch, trigger: 'blur' },
            ],
        },
    }
},

methods: {

    createAccount() {

        this.clearErrors();


        let passed = this.runValidator();

        if (! passed) return;


        axios.post(`/register`, this.form)
            .then(response => {

                EventBus.$emit('user-form-completed', this.form);

                return;

            })
            .catch(error => {

                const errors = error.response.data.errors;

                for (let index in errors) {

                    let error = errors[index][0];

                    this.errors[index] = error;
                }
            });
    },


    clearErrors() {
        this.errors = {...this.blankErrors};
    },


    runValidator() {

        let passed = false;

        this.$refs.form.validate((valid) => {
            if (valid) passed = true;
        });

        return passed;
    },
},
Sign up to request clarification or add additional context in comments.

Comments

0

I am using Laravel and I usually do like this, My validation in Laravel controller

    return Validator::make($data, [
        'email' => 'required|email',
        'password' => 'required|min:6',
    ]);

My vue.js code in if error comes

if(error.response.status == 400){
    let errors = error.response.data.errors;
    for(let i in errors){
        document.querySelector("div[for='"+i+"']").innerHTML = errors[i][0];
    }
}else if(error.response.status == 401){
    console.log(error.response);
    let errors = error.response.data;
    document.querySelector("div[for='password']").innerHTML = errors;
}

Complete vue component is

const Login = { 
template: `
    <div class="container">
      <div class="row row-body"> 
        <div class="col-12 col-md-6 offset-md-3">
          <div class="row">
            <div class="col-12 col-md-12 text-center">
              <h1>Login</h1>
            </div>
          </div>
          <div class="row">
            <div class="col-12 col-md-12">
              <form method="POST" action="">

                <div class="row pt-3 pb-3">
                  <div class="col-12 col-md-10 offset-md-1 form-group">
                    <input class="form-control form-rounded" placeholder="Email*" v-model="email">
                    <div for="email" class="text-danger"></div>
                  </div>
                </div>
                <div class="row pb-3">
                  <div class="col-12 col-md-10 offset-md-1 form-group">
                    <input class="form-control" placeholder="Password*" v-model="password" type="password">
                    <div for="password" class="text-danger"></div>
                  </div>
                </div>
                <div class="row pt-3">
                  <div class="col-12 col-md-12 form-group text-center">
                    <button @click="login" class="btn as-btn-outline as-btn-dark mx-2 my-2 my-sm-0 big-btn" type="button">LOGIN</button>
                  </div>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
`,
data(){
    return {
        email: '',
        password: ''
    }
},
mounted(){
    /**/
},
methods:{
        login: function(){
            var formdata = {};
            formdata.email = this.email;
            formdata.password = this.password;
            axios
                  .post('http://far.test/api/login',formdata)
                  .then(response => {
                    console.log(response.data);
                    if(response.data.token !== undefined){
                        this.$parent.is_auth = true;
                        sessionStorage.setItem('asset_token', response.data.token);
                        router.push({ path: '/home' });
                    }
                  })
                  .catch(error => {
                    if(error.response.status == 400){
                        let errors = error.response.data.errors;
                        for(let i in errors){
                            document.querySelector("div[for='"+i+"']").innerHTML = errors[i][0];
                        }
                    }else if(error.response.status == 401){
                        console.log(error.response);
                        let errors = error.response.data;
                        document.querySelector("div[for='password']").innerHTML = errors;
                    }

                  })
                  .finally(() => console.log('finally')/*this.loading = false*/);

        },
    }
}

And related laravel controller methods are

public function validateAuditLogin($data){
     return Validator::make($data, [
        'email' => 'required|email',
        'password' => 'required|min:6',
    ]);
}

public function loginForAudit(Request $request){
    $requestAll = $request->all();
    $pwd = base64_decode($requestAll["password"]);
    for($i=0;$i<4;$i++){
        $pwd = base64_decode($pwd);
    }
    $requestAll['password'] = $pwd;
    $validator =  $this->validateAuditLogin($requestAll);
    if($validator->fails()){
        return response()->json(['errors'=>$validator->messages()],400);
    }
    if ($user = \Auth::attempt(['email' => $requestAll['email'], 'password' => $requestAll['password'] ])) {
        $token = str_random(40);
        User::where('id',\Auth::id())->update(['api_token'=>$token]);
        return response()->json(['token'=>$token]);
    }else{
        return response()->json('Email or password is incorrect',401);
    }
}

5 Comments

Thank you for your code. Unfortunately I'm using element.eleme.io/#/en-US and that library has own input components and looks like I have no options to add something like <div for="password" class="text-danger"></div> by hands :(
@akor: Please provide any live demo ?
I just got idea, that I have to inspect DOM in order to add messages in your way. Let me check.
Yeah, I'll add something like that <div class="el-form-item__error">Please input Activity name</div> by hands. Thank you for inspiration :)
@akor, you may not have seen that Element added this into their form-item component and listed it in their documentation as of Nov. 19, 2016. github.com/ElemeFE/element/pull/1221/files/…
0

I figured out a Laravel solution based on Laracast project. The advantage of this approach is that the Error class is reusable.

In your component.vue,

  • Bind individual input error with errors.get('field') e.g :error="errors.get('email')"
  • Import Error class (see snippet below)
  • Add errors object to vue data
  • Make axios request and record response data

<template>
  <el-form  label-position="top"
            label-width="100px"
            :model="loginForm"
            :rules="rules"
            @submit.prevent="validateForm"
            ref="loginForm"
            status-icon validate-on-rule-change>

    <el-form-item label="email" prop="email" :error="errors.get('email')">
      <el-input   v-model="loginForm.email" placeholder="Enter your email"></el-input>
    </el-form-item>

    <el-form-item label="password" prop="password" :error="errors.get('password')">
      <el-input   v-model="loginForm.password" placeholder="Enter your password"></el-input>
    </el-form-item>

    <!-- Note about get() method in :error="errors.get('password')" see errors js -->

  </el-form>
</template>

<script>
  import { Errors } from './../templates/errors.js';
  export default {

    // Data
    data() {
      return {
        loginForm: {
          email: '',
          password: '',
        },

        // This is where we manage laravel errors
        errors: new Errors(),

        // Local validation disabled
        rules: {
          email: [
            { required: false, message: 'Please enter your email', trigger: 'blur' },
            // required set to false to for the sake of testing with laravel
          ],
          password: [
            { required: false, message: 'Please enter your password', trigger: 'blur' },
            // required set to false to for the sake of testing with laravel
          ],
        }
      };
    },

    // Methods
    methods: {

        // Validate form data
        submitForm(loginForm) {

// Clear Laravel errors before submitting form
this.errors.clear()

          this.$refs[loginForm].validate((valid) => {
            if (valid && ! this.errors.any()) {
              console.log('Data is validated. Submitting' + this.loginForm);
              this.login()
this.$refs[loginForm].validate()

            } else {
              console.log('Cannot submit, Invalid data');
              return false;
            }
          });
        },

        // post data
        login(){
          axios.post('/login'.login, this.loginForm).then( response => {
            // Data submitted successifully
          })
          .catch(error => {
            // There was an error like
            this.errors.record(error.response.data.errors)
            // Note: see errors.js for record method
          });
        }

    }
  }
</script>

Then import errors.js (credit Laracast project)

export class Errors {
    /**
     * Create a new Errors instance.
     */
    constructor() {
        this.errors = {};
    }


    /**
     * Determine if an errors exists for the given field.
     *
     * @param {string} field
     */
    has(field) {
        return this.errors.hasOwnProperty(field);
    }


    /**
     * Determine if we have any errors.
     */
    any() {
        return Object.keys(this.errors).length > 0;
    }


    /**
     * Retrieve the error message for a field.
     *
     * @param {string} field
     */
    get(field) {
        if (this.errors[field]) {
            return this.errors[field][0];
        }
    }

    /**
     * Retrieve flash message if any
     *
     * @param {string} field
     */
    getFlash(field) {
        if (this.errors[field]) {
            return this.errors[field];
        }
    }


    /**
     * Record the new errors.
     *
     * @param {object} errors
     */
    record(errors) {
        this.errors = errors;
    }


    /**
     * Clear one or all error fields.
     *
     * @param {string|null} field
     */
    clear(field) {
        if (field) {
            if (this.has(field)) {
                delete this.errors[field];
            }

            return;
        }

        this.errors = {};
    }
}

Comments

0

add a ref with any name and do the following:

In js do this
this.$refs.formData.errorBucket=[]
<v-text-field                                                                  
  ref="formData"
  :rules="[rules.required, rules.max]">
</v-text-field>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.