Vee-validate: How the this.$validator can be accessed in js file external(in a separate Helper) to the component itself?

Created on 19 Oct 2018  Â·  9Comments  Â·  Source: logaretm/vee-validate

Reference #951 (old issue, not really solved)

Description:

@logaretm How are you doing?
This question I do think is still open.
How I can access the this.$validator that is related with a vue component in an external js file?

I do have an helper (js file) in which I define an axios interceptor for the response. I am able to access the routes or the store without using this.$, but I cannot access the validator.

If I create anew instance of the validator (validator = new Validator()), I clearly don't have access to the component instance. The idea is to move the error catching in the interceptor, so also the validation.

I checked the mixin solution but it doesn't really work, without repetition of it in different components. And to be honest that will just make useless the idea of having a central interceptor as dispatcher for errors.

Do you have some suggestions on how access this.$validator inside of an axios interceptor located in an external file in relation with the component?

For now my solution is to move everything in the interceptor, except the validation error (this btw comes from the backend, and it's a 422 for existing username). I catch it with the axios normal error catch, and I do inside of the component.
However I am not really happy with this solution, because it requires the I repeat the same code in every component that has the same requirement. I would simply love to have it just in one place.

Moreover I do use the add functionality in order to display the errors in the same way that I do with the client side validation (The form fields are signed in red with the error also if coming from the backend).

✨ enhancement

Most helpful comment

I can think of some hacky solutions, since Vuex, and the Router rely on having a singular instance, opposite of vee-validate so its hard to centralize its behavior.

Assuming you have something like backend rule that pings your API and returns with the errors, you can mark the current active validator instance running that rule and can easily grab it in your interceptor.

For example here is some psudo code:

// vee-validate.js
export let activeValidator;

const originalValidate = Validator.prototype.validate;

// Monkey-patch the validate method.
Validator.prototype.validate = function (...args) {
  activeValidator = this; // mark the current validator instance.
  return originalValidate.call(this, ...args);
}

// interceptors.js
import { activeValidator } from './vee-validate.js';

function intercept (response) {
  if (response.statusCode === 422) {
    // assuming errors is an array of errors
    // you may need to do some transformations here
    activeValidator.errors.add(response.data.errors);
  }
}

This would give you access to the most recent validator that triggered the API call, which might be sufficient. In the meantime I will mark this as an enhancement for when I can work on it.

All 9 comments

I can think of some hacky solutions, since Vuex, and the Router rely on having a singular instance, opposite of vee-validate so its hard to centralize its behavior.

Assuming you have something like backend rule that pings your API and returns with the errors, you can mark the current active validator instance running that rule and can easily grab it in your interceptor.

For example here is some psudo code:

// vee-validate.js
export let activeValidator;

const originalValidate = Validator.prototype.validate;

// Monkey-patch the validate method.
Validator.prototype.validate = function (...args) {
  activeValidator = this; // mark the current validator instance.
  return originalValidate.call(this, ...args);
}

// interceptors.js
import { activeValidator } from './vee-validate.js';

function intercept (response) {
  if (response.statusCode === 422) {
    // assuming errors is an array of errors
    // you may need to do some transformations here
    activeValidator.errors.add(response.data.errors);
  }
}

This would give you access to the most recent validator that triggered the API call, which might be sufficient. In the meantime I will mark this as an enhancement for when I can work on it.

@logaretm Thank you for the suggestion!
In theory it should work, but in practice it doesn't.
The validator bag gets fill in the interceptor (using your monkey patch method), but it doesn't get passed to the component.
I don't think the patch really works as it is, I could fill the bag also with the new Validator.

The issue I do think is also related with the fact that in my component I need to use:
let vm = this; in order to access the this for the validator.

The validation is wrapped in a beforeSubmit validateAll, and if the validation pass, then the api post request is made with axios. In case there is some errors still on the backend (eg username already in use), the api return a response containing the errors and with a code of 422.
At this point, in the response the vm.$validator.errors.add gets triggered, and it loops in the response object adding to the existing validator the errors. This modus operandi allow me to display the validation errors (coming from the backend) exactly as I do using the vee-validate client side.
Moreover, with this technique I am also able to use the same messages customized in my vee-validator dictionary (I can access properties, names, messages etc).
The whole enchilada works pretty well, except clearly not in the interceptor.

I will try to modify the way in which I serve the http method, and see if moving it directly in the store will allow me to successfully use the method you kindly suggested.

Thank you for it!

You may also need to patch the validate all method since it has a different
code path than the validate method. You can also patch the _validate method
since it's used by all other methods. I will take another look later
tomorrow.

On Fri, 19 Oct 2018, 21:42 Massimiliano, notifications@github.com wrote:

@logaretm https://github.com/logaretm Thank you for the suggestion!
In theory it should work, but in practice it doesn't.
The validator bag gets fill in the interceptor (using your monkey patch
method), but it doesn't get passed to the component.
I don't think the patch really works as it is, I could fill the bag also
with the new Validator.

The issue I do think is also related with the fact that in my component I
need to use:
let vm = this; in order to access the this for the validator.

The validation is wrapped in a beforeSubmit validateAll, and if the
validation pass, then the api post request is made with axios. In case
there is some errors still on the backend (eg username already in use), the
api return a response containing the errors and with a code of 422.
At this point, in the response the vm.$validator.errors.add gets
triggered, and it loops in the response object adding to the existing
validator the errors. This modus operandi allow me to display the
validation errors (coming from the backend) exactly as I do using the
vee-validate client side.
Moreover, with this technique I am also able to use the same messages
customized in my vee-validator dictionary (I can access properties, names,
messages etc).
The whole enchilada works pretty well, except clearly not in the
interceptor.

I will try to modify the way in which I serve the http method, and see if
moving it directly in the store will allow me to successfully use the
method you kindly suggested.

Thank you for it!

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/baianat/vee-validate/issues/1656#issuecomment-431477020,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF-KSuQ2ZLM_bzUSc92JtfxMHL1O5F7cks5umis2gaJpZM4Xvm6T
.

@logaretm thank you for looking into it:) I do really appreciate it.

I do have a folder where I have all vee-validate things (config, dictionary etc). Inside of that folder I do have an index.js that I use to set up vee-validate inside of a vue.use (I define a const veeValidate that I default export). In this way I can import directly the const in my main.js file, and put inside my vue app, with store, router etc.

I do use this methodology to keep separated different modules configuration.
Same thing I do with other modules(including axios). The methodology is based more or less on the same idea that help separating routes, or stores in multiple files(makes more clear for me dealing with them) - and probably is related with my pythonic experience.
To be honest the technique works pretty well, and allow me to have a very well organized (at least from my point of view) code.

Under these premises, what I did with your snippet suggestion, was to create an extra file inside my vee-validate config module, and monkey patched the validator, after I exported the let, and I imported it as you suggested in my axios file(where I do have my api methods).
I am not really sure of this last passage, simply because I am not really sure my vue.use uses the patch or the patch it’s just issued on a completely new vue.use.

As you can see, the only way that I do have to let the system catching the errors coming from the server, and display them in the same way I do in the client side, is to be able to access the this.$validator or your activeValidator inside of my axios endpoint file. Unfortunately, using vuex actions, I do have to put the catch in the axios file, and I cannot really move anymore the vee-validate Logic that I used on my first post inside the component - meaning I cannot longer separate the catch as I did before only for 422 in order to show kind of client side errors messages.

For what concern your Monkey Patch suggestion, if you don’t mind, please explain a bit better where and how I should monkey patch. I don’t know, but maybe it’s just my misunderstanding, and I am applying the patch in a wrong way and instead your method works fine. To be honest I just used my intuition and I am not strongly convinced that it was correct.

Thank you again. Hope this is not bothering you so much, but I do think such feature it’s a good one to have, especially if the project becomes a bit more complex than a simple tutorial.

Monkey patching requires a little bit of knowlege about the source code, at first I suggested validate but again it doesn't cover the other methods. __So I suggest you patch the _validate method which is used internally by all APIs__ so it should cover: validate, validateAll, validateScopes and verify.

This isn't a solution, I'm merely suggesting a workaround, since like I pointed out the architecture difference between vuex/router and vee-validate is that vee-validate doesn't use a centralized validator/state, but we are moving towards that design slowly. So implementing a solution for this might take time and introduce breaking changes.

The monkey patch should work in theory but I do not have the time to build an example with it at the moment (serving in army mandatory service) so maybe in a couple of days I find the time to hack something for you.

You are not bothering at all, I'm always curious at the different ways developers use this library and the design choices for their app.

@logaretm hey man thank you for the answer. I do understand your army commitment:) and I do appreciate you took the time to write me back.

I agree with you, the monkey patch requires a bit more deep knowledge of the source code that I have, and will take me an insane effort to try to make it from scratch.

I do appreciate and I thank you for your willing to put together an example in the next couple of days. Sure thing that will help me, and probably will help me in crafting a nice workaround.
I do understand that the philosophy behind the library is very different from vuex or rooter, and I do understand that such functionality would be a breaking change(that is why I do appreciate very much your answer, the time you put into it, and your willing to help me out).

Looking forward to your example as soon as you get some time!

Thanks again...

@logaretm my friend never mind, I solved without monkey:) I am from my phone right now but I will post a more detailed explanation later from my laptop!

@logaretm Sorry for the delay:)
To resume: you can actually pass as second parameter, in the vuex callback, the component instance (vuex needs to be aware of the context), and that is passed to the axios method, allowing to trigger everything I was looking for.

There is no need to monkey patch vee-validate or centralize its behavior. Just passing the instance of the component as parameter solves the issue (at least for what I was trying to accomplish - maybe in other user case is not enough).

Great, then I guess we can close this then, maybe create a small gist and reference it here for future devs looking for similar use cases.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jagasan picture jagasan  Â·  3Comments

Youdaman picture Youdaman  Â·  3Comments

YamenSharaf picture YamenSharaf  Â·  3Comments

MeltedFreddo picture MeltedFreddo  Â·  3Comments

saintplay picture saintplay  Â·  3Comments