Vee-validate: Clear errors after form submition

Created on 26 Dec 2016  路  27Comments  路  Source: logaretm/vee-validate

Versions:

  • VueJs: 2.1.7
  • Vee-Validate: 2.0.0-beta.18

Description:

Once I submit the form I clear the fields and I want to clear the errors as well, but I'm having a hard time trying to do that.

Steps To Reproduce:

Form:

<form role="form" class="form-horizontal" @submit.prevent="persistData">
            <legend>Supervisor & Deputy Requests</legend>

            <div class="form-group">
                <label for="requesttype" class="col-sm-2 control-label">Request type</label>
                <div class="col-sm-10">
                    <select v-model="form.requesttype" name="form.requesttype" id="requesttype" class="form-control"
                            data-vv-rules="required" v-validate.initial="form.requesttype">
                        <option value=""></option>
                        <option value="Option 01">Option 01</option>
                        <option value="Option 02">Option 02</option>
                    </select>
                    <span v-show="errors.has('form.requesttype')" class="text-danger">This field is required</span>
                </div>
            </div>
            <div class="form-group">
                <label for="userid" class="col-sm-2 control-label">User ID</label>
                <div class="col-sm-10">
                    <input v-model="form.userid" name="form.userid" type="text" class="form-control" id="userid"
                           data-vv-rules="required"
                           v-validate.initial="form.userid">
                    <span v-show="errors.has('form.userid')" class="text-danger">This field is required</span>
                </div>
            </div>
            <div class="form-group">
                <label for="requestdescription" class="col-sm-2 control-label">Request description</label>
                <div class="col-sm-10">
                    <textarea v-model="form.requestdescription" name="form.requestdescription" class="form-control"
                              id="requestdescription" cols="30"
                              rows="10" data-vv-rules="required"
                              v-validate.initial="form.requestdescription"></textarea>
                    <span v-show="errors.has('form.requestdescription')"
                          class="text-danger">This field is required</span>
                </div>
            </div>

            <button type="submit" class="btn btn-primary pull-right">Submit</button>
        </form>

Persist data method:

                let self = this
                // Validate All returns a promise and provides the validation result.
                this.$validator.validateAll().then(success => {
                    if (!success) {
                        // handle error
                        console.log('Error!')
                        return
                    }
                    axios.post('/static/api/SupervisorDeputyRequest/persistdata.php', queryString.stringify(self.form))
                        .then(res => {
                            self.form = {
                                requesttype: '',
                                userid: '',
                                requestdescription: ''
                            }
                        })

I want to clear the errors in the AJAX callback. This code is in a .vue file and vee-validate is being imported in the main.js file.

Most helpful comment

Hey,
I was having issues with clearing form inputs and resetting the errors much like a few here.
I'd basically set inputs to null on submit and then run this.$validator.reset(), it didn't work.

The thing you have to remember is javascript is naturally asynchronous so, when you fire a function it's running all the code at the same time, or attempting to, and that's my rough understanding.

SO what I did was run an async function :

const clear = async () => {
    this.contact.firstName = null
    this.contact.lastName = null
    this.contact.email = null
    this.contact.message = null
}

And then call it that function. After it has completed I call the rest:

clear().then(() => {
    this.$validator.reset()
})

And it works for me. Hopefully that helps someone else.

All 27 comments

have you tried:

this.errors.clear();

http://vee-validate.logaretm.com/api#error-bag

I did, but it doesn't work. It seems that "errors" doesn't have a clear() method when I call it. This is what I see if I print "this.errors":

vue-validation

Perhaps I'm missing something?

methods should appear in the __proto__ property.

I'm not sure what could be wrong, but here is a fiddle that seems to be working fine:

https://jsfiddle.net/Lt33ep6e/1/

Actually what I was missing is the v-validade directive. I was adding v-validate.initial="field" instead =/. I had misunderstood that part. Sorry about that and thanks for your time and great job on this plugin.

Its okay, glad it worked.

@badcom I have this problem. Can you explain how you solved it?

I've showed above what the problem was. If you paste your code here for in fiddle js I can try and help.

@logaretm why when I do this from my code it does not work:

this.$http.post('/api/services', {name: service}).then( ( response ) => {
                    this.service = "";
                    this.buttonMessage = 'Guardar';
                    this.REQUEST_ON_WAY = false;
                    this.errors.clear();

}

image

I still see the errors even after the request is over.

But if from the console I do this:
image
The errorBag does clear successfully.
image

Very confused right now.

I can't tell the problem from your snippet, I guess something else happens after the request is completed, like you could be resetting the form after the request completes which triggers validation.

If that is the case you can move the clear method call to after the form reset. otherwise can you provide me with a larger sample of your code? maybe just the component code.

@logaretm
This is all the code from my component.

<template>
    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title" id="myModalLabel">Nuevo Servicio</h4>
                </div>
                <div class="modal-body">
                    <form>
                        <div class="form-group">
                            <label for="serviceName">Nombre</label>
                            <div class="input-group">
                                <span class="input-group-addon"><i class="fa fa-thumb-tack" aria-hidden="true"></i></span>
                                <input type="text" name="service" v-model="service" v-validate="service" data-vv-rules="required|min:4" data-vv-as="El servicio"  placeholder="Escribe aqu铆 el nombre el nombre del servicio" class="form-control" id="service" autocomplete="off">
                            </div>
                            <p class="text-danger form-error" v-show="errors.has('service')">{{ errors.first('service') }}</p>
                        </div>
                        <div class="form-group">
                            <button type="button" class="btn btn-default is-warning" data-dismiss="modal">Cancelar</button>
                            <button type="submit" class="btn btn-default is-primary" :class="{loading: REQUEST_ON_WAY}"  :disabled="REQUEST_ON_WAY || errors.any()" @click="addService(service)">{{buttonMessage}} <i :class="REQUEST_ON_WAY ? 'fa fa-spinner fa-spin' : 'fa fa-plus'" aria-hidden="true"></i></button>
                            <span v-show="errors.any()" class="text-danger form-error">Check the form for errors <i class="fa fa-exclamation-triangle" aria-hidden="true"></i></span>
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default{
    created(){

    },

    mounted(){

        // Focus the input when modal is loaded.
        $('#myModal').on('shown.bs.modal', () => {
            $('#service').focus();
        });

    },

    data(){
        return {
            buttonMessage: 'Guardar',
            service: '',
            REQUEST_ON_WAY: false,
        }
    },

    methods: {

        addService(service){
            // Run the validation
            this.$validator.validateAll().then( (success) => {
                if (! success) {
                    alert('Hay errores en el formulario');

                    return;
                }

                // If there is no error.
                let parent = this.$parent
                this.REQUEST_ON_WAY = true;
                this.buttonMessage = 'Cargando...';

                this.$http.post('/api/services', {name: service}).then( ( response ) => {
                    this.service = "";
                    this.buttonMessage = 'Guardar';
                    this.REQUEST_ON_WAY = false;
                    this.errors.clear();

                }, (response) => {
                    let ERROR_MESSAGE = 'No se ha guardado el servicio';
                    let ERROR_HEADER = 'Error';

                    if (response.body.SERVICE_EXISTS == true) {
                        ERROR_MESSAGE = 'Ya existe un servicio con ese nombre';
                        ERROR_HEADER = 'Duplicaci贸n';
                    }
                    parent.$options.methods.createOverlayAlert(ERROR_HEADER, ERROR_MESSAGE, 'error');
                    this.buttonMessage = 'Guardar';
                    this.REQUEST_ON_WAY = false;
                });


            });

        },

    }

}
</script>

In the addService method is where I save the service via Ajax.

Probably because the changes to the model value is being done slower that the errors being cleared, you can append another .then in which you can clear the errors.

here is a small example: https://jsfiddle.net/hcac5je0/1/

@logaretm Thanks so much. This seemed to fix my problem. Kinda funny how this was affecting me.

Just stumbled upon this thread when a friend asked me for help. I'd recommend you guys read more about Vue's reactivity system and async update queue in particular.

The gist is that model changes are not flushed immediately when you change a particular model property. Changes are batched and deferred to the next tick for optimal performance. Vue provides nextTick hook that allows you to execute code right after the reconciliation had been performed. It's probably a better idea than using setTimeout directly because it is a publicly documented API which will always yield the best and most reliable results.

EDIT: Polished the text and reinforced the reasoning.

@mareksuscak thanks for your comment, you are correct but I'm not using timeout to clear the errors, its to emulate a server response time. I just chain another then callback and it seems to work fine.

but you are correct, nextTick should be preferred, since it is there to handle such cases.

In my case, i forgot to add the scope of the form i wanted to reset the errors for.
this.errors.clear('my-scope') did the trick.
Just fyi.

I'm still struggling with this problem. I'm using this.$nextTick() to only reset the validator after model changes are flushed, but this is still too fast, since the validation error messages appear below the fields right after they are cleared. Using a timeout of 1000ms instead of $nextTick does do the trick, but is less nice because it is so slow that you see the validator messages appear and disappear again....

~javascript
// Called after a succesfull post request
reset(){
// Clear the form fields
this.accommodation = {...template.fields};
// Reset the validator after the next tick
this.$nextTick()
.then(() => {
this.$validator.reset();
this.errors.clear();
});
}
~

Any idea what I'm doing wrong? I found a mention of this issue on several locations, but none with a conclusive solution (and all were quite dated: $validator.clean has been depecrated in favor of $validator.reset in the mean time).

Hey,
I was having issues with clearing form inputs and resetting the errors much like a few here.
I'd basically set inputs to null on submit and then run this.$validator.reset(), it didn't work.

The thing you have to remember is javascript is naturally asynchronous so, when you fire a function it's running all the code at the same time, or attempting to, and that's my rough understanding.

SO what I did was run an async function :

const clear = async () => {
    this.contact.firstName = null
    this.contact.lastName = null
    this.contact.email = null
    this.contact.message = null
}

And then call it that function. After it has completed I call the rest:

clear().then(() => {
    this.$validator.reset()
})

And it works for me. Hopefully that helps someone else.

I'm going to try that out as soon as I'm able to. Seems pretty straightforward and logical to me. Maybe they should put an entry about this in the documentation?

Alas, it doesn't work for me and the same problem still persists.
~~~javascript
import template from '../../templates/accommodation';
...
methods:{
save(){
this.$validator.validateAll()
.then(result => {
if(result === false) return;
this.$emit('clicked-save', this.accommodation);
});
},
reset(){
const clear = async () => {
this.accommodation = {...template.fields};
};

    clear().then(() => {
        console.log('Resetting validator');
        this.$validator.reset(); 
    });
}

}
~~~

I store all fields and their default values in separate template files and clone that object again when I reset the form. It might be that this action negates this solution.
Regardless, I solved this problem for now by adding a short timeout before calling $validator.reset(); not very elegant (as you still see the validation errors for a short time), but it does do the trick. You're right at least that this seems to be a concurrency issue.

Got a little more info on this. I have configured vee-validate with this setting:
~javascript
Vue.use(VeeValidate, {
inject: false,
delay: 500
});
~

So, obviously, vee-validate validates the fields 500 ms after they change. This problem might be a race condition between:

  1. The reset() function
  2. The validation function that is triggered with a timeout of 500 ms once the values of the fields change.

I think this could be solved by letting the reset() function also cancel all function that are fired with a timeout or interval?

I had to modify the code provided by @devanflaherty to basically wait for two event loop iterations. I reset the data, waited for nextTick, resolved and waited for another nextTick in the then callback. Code below:

methods:{
  clearFormData () {
    return new Promise(resolve => {
      Object.assign(this.$data, this.$options.data.call(this));
      this.$nextTick().then(() => resolve());
    });
  },
  resetForm () {
    return new Promise(resolve => {
      this.clearFormData().then(() => {
        this.$nextTick().then(() => {
          this.$validator.reset();
          this.errors.clear();
          resolve();
        });
      });
    });
  }
}

Not the most performant solution, but it seems to be a stable stopgap for the moment.

Also, FYI @dschreij, notice the line where I reset the data in the component. Using Object.assign(this.$data, this.$options.data.call(this)); will reset the components data to its default values. Its a useful shorthand way of doing it.

I used Promise.prototype.then() and worked for me.

I created a method called _clear_ for clear all inputs and other called _createMessage_ for submit:

  methods: {
       clear () {
          this.email = ''
          this.message = ''
          this.subject = ''
        }
        createMessage: function (event) {
            this.$validator.validateAll().then(function (result) {
               if (result) {
                  ...Your Mutation...

                    new Promise(function (resolve, reject) {
                        resolve('Reset Form!')
                        that.clear()
                    }).then(() => {
                       that.$validator.reset()
                    })
              }//if
          )}//validator
      }//createMessage
  }//methods

Sorry about the confusing reset behavior guys, Its being already wrapped in a vm.$nextTick call which should in theory reset them correctly but it doesn't work out, I probably will wrap it instead in a setTimeout for 2 ticks instead, since double wrapping the reset in a $nextTick seems to work in my tests due to vue needing two ticks, one to update the fields, another to update the errors.

I should be able to get a quick release today with these changes

Is there a good and easy solution? I struggle with the same problem.

cancel: function () {
    this.comment = this.newComment();
    this.errors.clear();
}

The text in the form disappears but after that the error that the comment body can't be empty comes up again. :( I've tried to wrap the two lines inside a nexttick() callback but this didn't made it work either.

@burzum errors.clear isn't enough, as it doesn't reset the validator state like flags and does not respect Vue ticks, use validator.reset instead:

https://jsfiddle.net/logaretm/2rrbzyek/

@logaretm Your solution works like a charm. Thanks a lot

just do it:
this.errors.items=[]
(you may see THE ERRORS like {{errors}} and understand - what you must CLEAR)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

the94air picture the94air  路  3Comments

Shchepotin picture Shchepotin  路  3Comments

yyyuuu777 picture yyyuuu777  路  3Comments

triffer picture triffer  路  3Comments

parweb picture parweb  路  3Comments