Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear errors after form submition #209

Closed
badcom opened this issue Dec 26, 2016 · 27 comments
Closed

Clear errors after form submition #209

badcom opened this issue Dec 26, 2016 · 27 comments

Comments

@badcom
Copy link

badcom commented Dec 26, 2016

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.

@logaretm
Copy link
Owner

logaretm commented Dec 26, 2016

have you tried:

this.errors.clear();

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

@badcom
Copy link
Author

badcom commented Dec 26, 2016

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?

@logaretm
Copy link
Owner

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/

@badcom
Copy link
Author

badcom commented Dec 27, 2016

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.

@logaretm
Copy link
Owner

Its okay, glad it worked.

@wilburpowery
Copy link

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

@badcom
Copy link
Author

badcom commented Jan 17, 2017

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

@wilburpowery
Copy link

@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.

@logaretm
Copy link
Owner

logaretm commented Jan 20, 2017

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.

@wilburpowery
Copy link

wilburpowery commented Jan 20, 2017

@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.

@logaretm
Copy link
Owner

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/

@wilburpowery
Copy link

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

@mareksuscak
Copy link

mareksuscak commented Feb 15, 2017

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.

@logaretm
Copy link
Owner

@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.

@hurradieweltgehtunter
Copy link

hurradieweltgehtunter commented Nov 6, 2017

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.

@dschreij
Copy link

dschreij commented Dec 5, 2017

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....

// 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).

@devanflaherty
Copy link

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.

@dschreij
Copy link

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?

@dschreij
Copy link

dschreij commented Jan 2, 2018

Alas, it doesn't work for me and the same problem still persists.

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.

@dschreij
Copy link

dschreij commented Jan 2, 2018

Got a little more info on this. I have configured vee-validate with this setting:

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?

@JavaTheNutt
Copy link

JavaTheNutt commented Jan 5, 2018

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.

@anykarolyne
Copy link

anykarolyne commented Jan 8, 2018

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

@logaretm
Copy link
Owner

logaretm commented Jan 9, 2018

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

@burzum
Copy link

burzum commented Jan 15, 2018

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.

@logaretm
Copy link
Owner

@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/

@david95thinkcode
Copy link

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

@Mikle
Copy link

Mikle commented Jun 7, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests