I am working on upgrading from VeeValidate v2 to v3. Since they have removed the ErrorBag concept, I am struggling to figure out how to handle backend validation.
Previously (see code below), I was just running client-side validation, if that passed, call a server validation route, if that failed I would just use the errors.add function in VeeValidate.
Any help would be appreciated. Just need to know to accomplish backend validation handling in VeeValidate v3. Thanks!
validateStep(step) {
this.$validator.validateAll(step).then((result) => {
// If client-side validation passes, move into this block.
if (result) {
// Then run server-side validation.
axios
.post(`/ajax/validate-stuff`, this.postData)
// If server-side validation Passes:
.then(function (response) {
// Do the things
})
// If server-side validation Fails:
.catch(function (error) {
// Add errors to VeeValidate Error Bag
var entries = Object.entries(error.response.data.errors);
entries.forEach(function(item, index) {
this.Errors.add({
field: item[0],
msg: item[1][0]
});
});
});
}
});
}
The answer by fylzero above is correct. The important point is to ensure that the vid in the validation provider (below it is 'testinput') matches the key in the error object returned by the server. You then catch the error:
<validation-observer v-slot="{ invalid }" ref="formValidator">
<form>
<validation-provider
v-slot="{ errors }"
vid="testinput"
>
<input />
<span>{{ errors[0] }}</span>
</validation-provider>
</form>
</validation-observer>
<script>
try {
// Make the api call here
}
catch (error) {
// populate the vee-validate error manually
this.$refs.formValidator.setErrors(error.data.errors);
}
</script>
I also posted an issue for this in the Github for VeeValidate and was provided the answer.
The documentation for this is, at the time of writing this, buried in the examples section:
https://logaretm.github.io/vee-validate/examples/backend.html#server-side-rules
I was told this will be updated in the proper documentation shortly.
Updated Link: https://vee-validate.logaretm.com/v3/advanced/server-side-validation.html#handling-backend-validation
Related
We have followed the Google Identity migration documentation and replaced the new code but used existing google OAuth client ID.
Previously, before migration we used to get a warning in console, please see the image attached-
Now, after we have added the new code the warning message is not showing in the console. Below is the new console image
But still we have received one reminder mail from Google with our app name and OAuth key that we still need to migrate.
So we are confused if the migration was successful or not? Please help? Do we have to create a new OAuth client ID and replace?
(Please note we are not seeing any warning in console related to this.)
Here is our code snippet-
<div class="bg-google text-white pt-3 pb-2 px-4 rounded-lg cursor-pointer ml-4 mr-4" #click="SocialLogin">
<img src="#/assets/images/pages/login/google.svg"/><span>Sign in with Google</span>
</div>
mounted() {
this.tokenClient= google.accounts.oauth2.initTokenClient({
client_id: process.env.VUE_APP_GOOGLE_OAUTH_KEY,
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
prompt: "consent",
callback: tokenResponse => this.handleCredentialResponse(tokenResponse) // your function to handle the response after login. 'access_token' will be returned as property on the response
});
},
methods: {
SocialLogin() {
this.tokenClient.requestAccessToken();
},
async handleCredentialResponse(response) {
this.$helper.showLoading();
const userInfo = await new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', `https://www.googleapis.com/oauth2/v3/userinfo`);
xhr.setRequestHeader('Authorization', `Bearer ${response.access_token}`)
xhr.onload = function () {
if (this.status >= 200 && this.status < 300)
resolve(JSON.parse(this.responseText));
else resolve({ err: '404' });
};
xhr.send();
});
if(userInfo){
localStorage.setItem("user_email", userInfo.email);
}
},
}
I checked with the team apparently there is a back log for these messages so they may have detected a few weeks ago that you were still using the old library and the message is just getting sent now.
You know you have upgraded and are no longer getting the error message so your all good.
I am building a headless SPA SSR in NuxtJs, and I am wondering what best practices is for making sure that the application only loads if connection has been established to the remote API.
This is currently my index.vue component:
<template>
<div class="wrapper">
<div class="content">
<div class="title">
<h1>{{site.meta.title}}</h1>
</div>
</div>
</div>
</template>
<script>
import Meta from '../classes/api/General/Meta'
export default {
data () {
return {
meta: null
}
},
created () {
Meta.getMeta().then((response) => {
this.meta = response.data
})
}
}
</script>
This sometimes resolves in that site.meta.title is undefined because the site is loading before the api data has been initialised. And yes, site.meta.title is defined under the hood in the api. So. Next step I was thinking was using async like following script:
<script>
import Meta from '../classes/api/General/Meta'
export default {
data () {
return {
meta: null
}
},
async created () {
await Meta.getMeta().then((response) => {
this.meta = response.data
console.log(response.data.site.logo)
})
}
}
</script>
Though this doesn't help anyway.
But with v-if="meta" it does help. Though: now it seems that Axios is not rendering the content in the code (ssr) anymore.
console.log is not something that you can really trust 100% of the time for async debugging tbh.
console.log(JSON.parse(JSON.stringify())) can help a bit more but it's still have some drawbacks sometimes.
As for best practices, both beforeCreate and created do run on both sides (server + client) so it's fine to use those. You can also use asyncData and the new fetch (it's a Nuxt hook, not the actual fetch API).
Beware of using the async/await syntax properly tho (no need for then here):
async created() {
const response = await Meta.getMeta()
this.meta = response.data
console.log(this.meta)
}
Also, with proper async/await, this one will never happen actually
because the site is loading before the api data has been initialised
You can read a bit more about the Nuxt lifecycle here: https://nuxtjs.org/docs/2.x/concepts/nuxt-lifecycle
I'd recommend usually going the async fetch() way (non blocking, nice helpers), or async asyncData() if you need a blocking method. Middlewares can also be useful to make the whole process more flexible around your application.
You can get the whole explanation between the differences of fetch vs asyncData here: https://nuxtjs.org/blog/understanding-how-fetch-works-in-nuxt-2-12/
And if you want to have an example on how to use fetch in a real world scenario, you can read this one: https://nuxtjs.org/blog/build-dev-to-clone-with-nuxt-new-fetch/
So, It turns out that I got this the completely wrong way around.
In the newest nuxt versions, async fetch method is now included (build in).
With this, all rendering etc works fine and as expected.
My ended working code looks like this now:
<script>
export default {
async fetch () {
this.meta = await fetch(
'https://testurl.com/meta'
).then(res => res.json())
},
data () {
return {
meta: null
}
}
}
</script>
And the beauty with fetch, is that you can add listeners" like so:
<p v-if="$fetchState.pending">Fetching site</p>
<p v-else-if="$fetchState.error">Error happened</p>
<p>This content will be rendered server side even though waiting time</p>
I'm just posting this because my original question was a bit miss explained, and hope to help somebody else.
Edit:
I have marked kissu as answer (did see the post after i created this one), because it was explained so nice and well done!
Thanks :-)
I am using Vue (server side rendered) with mjml to generate emails.
So I have something (overly simplified) like:
<mjml><mj-body>Hello {{ User.Name }}</mj-body></mjml>
If the model doesn't define User then Vue throws an error and the whole output is lost.
What I want to the output to be along the lines:
<mjml><mj-body>Hello <error>'User' is undefined</error></mj-body></mjml>
I have implemented Vue.config.errorHandler but that just tells me about the error -- there is no rendered output.
Anyway to implement the equivalent of an error handler around each variable substitution?
If you are using Vue version >= 2.5, you can use errorCaptured to create an ErrorBoundary
const ErrorBoundary = {
name: 'ErrorBoundary',
data: () => ({
error: false,
msg: ''
}),
errorCaptured (err, vm, info) {
this.error = true
this.msg = `${err.stack}\n\nfound in ${info} of component`
},
render (h) {
return this.error
? h('pre', this.msg)
: this.$slots.default[0]
}
}
and use this in your component
<error-boundary>
<mjml><mj-body>Hello {{ User.Name }}</mj-body></mjml>
</error-boundary>
If the application has any javascript error, it will be displayed on UI
Example on codepen
If you want to have more user-friendly error, you can customize ErrorBoundary to have fallback to another component. You can find out in this tutorial
Another good example of using errorHandler
I have a Vue (2.2.1) component that should display a membership directory by making a request to a Laravel API. The request succeeds, and the response is correct when I console.log it. Likewise, the API request succeeds in Postman and the JSON object is correctly formatted. But, the response won't pass into the variable in the Vue instance data object to which I attempt to assign it. My code:
<template>
<div>
{{ entries }}
</div>
</template>
<script>
export default {
name: 'directory',
data() {
return {
entries: []
}
},
created() {
this.getEntries()
},
methods: {
getEntries: function() {
axios.get('/api/directory')
.then(response => {
this.entries = response.data;
});
}
}
}
</script>
When I load the page (after npm run dev), empty brackets representing the initial state of entries (an empty array) is displayed and not the raw JSON data. (I'm just testing now, not yet styling or building the table). However, if I add a console.log(this.entries) or console.log(response) or console.log(response.data), the correct form of the JSON is displayed in console and there are no errors.
I have replaced the axios code with the appropriate fetch code, including the ->json() line and get the same results. I have also used a "practice" API (JSONPlaceholder) to make sure it wasn't a problem with my API and likewise got the same results. This suggests that I'm doing something wrong in my Vue component <script>, but after hours of searching SO, Google, and Vue.js forum, I'm not finding anything. The closest matching link (Vue.js cannot set data returned from external axios response) was of no help, unfortunately.
I would make your entries a computed property so it will update on the call, it seems like your template is not updating a computed property may solve this.
Try this:
data(){
return {
dataEntries: null
}
computed: {
entries(){
if (dataEntries) return dataEntries
return []
}
I have a API login service using a http service to perform a login logic (LoginApiService, login-api.service.ts):
login(data: LoginCredentials): Observable<LoginResult> {
let body = JSON.stringify( data );
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(`${this.getBaseUrl()}/login`, body, options)
.map(this.extractData)
.catch(function(error: Response){
return Observable.throw(error); // IF YOU TRY TO LOGIN WITH WRONG CREDENTIALS, AN ERROR WILL BE THROWN
});
}
This service is used in a global service class (authService, auth.service.ts):
login(data: LoginCredentials): void {
this.api.login(data).subscribe(
data => {
this.isLoggedIn = true;
this.group = data.group;
this.Login.emit(new LoginResult(this.group)); // THE CALL OF THIS FUNCTION IS IGNORED IF THE "this.Login.error" FUNCTION HAS BEEN CALLED BEFORE
},
error => {
this.Login.error(error); // THIS IS IGNORED TOO, IF IT WAS CALLED BEFORE
}
);
}
Component (LoginComponent, login.component.ts):
ngOnInit() {
this.LoginSubscription = this.authService
.Login
.subscribe( // THE EVENTS ARE NOT FIRED IF THE ERROR EVENT HAS BEEN FIRED BEFORE ONCE
data => {
if ( this.authService.isLoggedIn ) {
this.router.navigate(['/dashboard']);
}
},
error => {
this.handleError(error);
}
);
}
login() {
this.authService.login( new LoginCredentials(this.user, this.password) );
}
Template (login.component.html):
<div>
<label>User: </label>
<input [(ngModel)]="user" placeholder="user">
</div>
<div>
<label>Password: </label>
<input [(ngModel)]="password" placeholder="password" type="password">
</div>
<p>
<button (click)="login()">Login</button>
</p>
If the error event function of the login observable has been called before, calling the emit and/or error function after this will be ignored.
With correct login credentials, the simulated API responses with HTTP
code 200 and everything works fine
On wrong credentials, the HTTP response is 500
After calling the API again (with correct or wrong credentials), the
events are not fired anymore
This means: If you use wrong login credentials you won't be able to try again without re-loading the page.
Is my idea of using observables wrong?
Why is the event stream hung up after calling the error function
once?
Could someone give me a hint to solve this problem (some kind of
workaround eg.)?
This is happening because you are erroring out your EventEmitter. EventEmitters are basically Observables and when it's Observer calls error or complete, it closes the Obersvable. Closed Observables will stop emitting events by design.
To fix your problem, remove this.Login.error(error); in the LoginComponent. so that you are not closing that EventEmitter. Try replacing that with logic to tell the user that the credentials are wrong or some other message to describe the error.