Vuelidate never v.dirty always true even if all requirements are met - javascript

Showing only relevant code, if more is needed tell me.
My problem is that even when a name is entered to the text-field. I cannot go to the next card since !this.$v.$invalid never becomes false.. I have no idea what i am missing here..
Note that the errors are showing fine when clicked on the button without it having any value.
Vuelidate.js
import Vuelidate from 'vuelidate'
import VueTimepicker from 'vue-time-picker'
Vue.use(Vuelidate)
CardOne.js
import { validationMixin } from 'vuelidate'
import { required, maxLength ,email } from 'vuelidate/lib/validators'
mixins: [validationMixin],
validations: {
name: { required, name },
},
methods {
showNextCard(){
this.$v.$touch() //it will validate all fields
console.log(this.$v);
if (!this.$v.$invalid) { //invalid, becomes true when a validations return false
//you dont have validation error.So do what u want to do here
console.log("in here");
this.$emit("nextCard");
}
},
}
computed {
name: {
get () {
return this.$store.state.cardOne.name;
},
set (value) {
this.$store.commit('updateName', value)
}
},
nameErrors () {
const errors = []
if (!this.$v.name.$dirty) return errors
!this.$v.name.required && errors.push('Måste fyllas i')
return errors
},
}
CardOne.html
<div >
<form>
<v-text-field
v-model="name"
label="För- och efternamn"
required
#blur="$v.name.$touch()"
:error-messages="nameErrors"
:v="$v.name">
</v-text-field>
<v-btn
v-bind:style="[formCardIndexx === 0 ? {'margin-left': 'auto'} : {}]"
v-if="formCardIndexx != 10"
active-class="no-active"
class="next-button"
type="button"
:ripple="false"
#click="showNextCard()">
NÄSTA
</v-btn>
</form>
</div>
I saw that i had added that when i checked to see. But it did not help the problem. This is the v object printed right before it should validate the form.

In validations, you have:
validations: {
name: { required, name },
},
required makes sense and is a valid vuelidate built-in validator per https://vuelidate.js.org/#sub-builtin-validators. However, name doesn't. Unless you have a customer name validator defined somewhere in code you aren't showing, you should try removing that. It could be failing to validate because it can't validate against undefined, which from what I see in your code, name would surely evaluate to.
Try:
validations: {
name: { required },
},

Related

Vue3: How to use Vuelidate v$ object in method

We are currently migrating an externally developed Vue 2 application to Vue 3 to fix issues with deprecated libraries and to be more 'future-proof'. Due to lack of knowledge about the Vue framework and the differences between Vue 2 and 3 (we read the migration guide), we are stuck on the use of the Vuelidate Next library to validate form fields.
We make use of:
Vue 3.2.13
#Vuelidate/core 2.0.0
#vuelidate/validators 2.0.0
The below code describes the situation as created by the original programmers in Vue 2 with minor adjustments. First we import the Vuelidate libraries and the form compoments we want to pass data and validation rules to. Next in setup(), we initiate Vuelidate by calling the useVuelidate hook and binding it to v$. Then in data(), the form fields, based upon a JSON schema, are defined. In validations(), we define a single rule for the 'nameEng' form field. Finally created() calls upon the setValidation method from the methods section.
<template>
<div v-if="facility">
{{ /* When we echo v$ we do get the full object */ }}
{{ v$ }}
<form #submit.prevent>
<component v-for="field in fields" :is="field.type" :key="field.id" :value="facility.fieldValues[field.id]"
:path="[field.id]" :context="{ resource: 'facilities', institutionId: facility.institutionId }"
:label="field.label" :fields="field.fields" :editable="editable" v-bind="{ ...field.options }" />
</form>
</div>
</template>
<script>
import useVuelidate from '#vuelidate/core';
import { fetchInstitution } from '#/services/institutionsService';
import FieldRow from '#/modules/core/components/ui/formElements/FieldRow.vue';
/* The JSON schema fields aka:
{
"general": [
{
"type": "FieldRow",
"id": "nameEng",
"label": "Facility name (English)",
"options": {
"field": "StringField",
"fieldOptions": {
"errorMessage": "Please fill in a name for this facility"
}
}
},
}
*/
import fields from '../schemas/fields.json';
export default {
components: {
FieldRow,
},
props: {
facility: {
type: [Object, Array],
},
editable: {
type: Boolean,
default: false,
},
},
setup() {
const v$ = useVuelidate();
return { v$: v$ };
},
data() {
return {
fields: fields.general,
};
},
validations() {
return {
facility: {
fieldValues: {
nameEng: {
required,
},
},
},
};
},
created() {
this.setValidation();
},
methods: {
setValidation() {
this.fields = fields.general.map((field) => {
if (field.options.fieldOptions?.errorMessage) {
/* Returns empty object: {} */
console.log(this.v$);
field.options.fieldOptions.validation = this.v$.facility.fieldValues[field.id];
}
return field;
});
},
},
};
</script>
The setValidation method needs to add the validation rules to the data which gets passed on to a form field component as props. However, when trying to access the v$ in the method, it is a completely empty object. We expected to be able to call v$.facility to get the rule set, but this logically results in an undefined. Contrary, when we echo v$ in the HTML template, we do get the full object returned including the facility rule set.
Our questions would be: why can we not use the v$ object in the methods section like this? should this method still be the correct way achieve the desired result in Vue 3?
Thanks in advance!
~ Tom

Vuelidate "required" ignoring value attributed in JS

I created a wizard form which uses Vuelidate to validate it's fields. The big majority of the fields have only the "required" function, so my validations are something close to this:
validations() {
if (this.currentStep === 0) {
return {
person: {
name: {
required,
},
age: {
required,
},
}
}
} else if (this.currentStep === 1) {
return {
person: {
street: {
required,
},
city: {
required,
},
state: {
required,
},
}
}
The thing is, I am receiving this data from an API, so the user can either fill the fields himself, or let the machine do it for him. When I receive the data, I make this attribution in a function in JS close to the following:
attributeData():
this.person.name = apiData.name;
this.person.age = apiData.age;
this.person.street = apiData.street;
this.person.city = apiData.city;
this.person.state = apiData.state;
If the user types the info, then everything works fine. If I receive the info from the API, then I get the error as if the input was empty.
This is how every field in my HTML is organized:
<b-form-group label="Name">
<b-form-input
v-model="person.name"
type="text"
size="sm"
:class="{
'is-invalid':
submitted && $v.person.name.$error,
}"
></b-form-input>
<div
v-if="submitted && $v.person.name.$error"
class="invalid-feedback"
>
<span v-if="!$v.person.name.required"
>Name is required.</span
>
</div>
</b-form-group>
Any idea of why Vuelidate can't recognize the values when I attribute them directly in JS? I've made this exact same way in another page and it worked. In DevTools even the $model of Vuelidate has the value that came from the API.
This error may occur if you have two elements using the same identifier.
Example:
data: {user_id: null} and setted v-model="user_id" in one input.
And another element input with: id:user_id
Beware if you are not manipulating the value and then it lost the reference, example:
data: {user: {name: null}}
And you filled it by API but latter in created or mounted put something like:
this.user = {}
The reference user.name was gone and validation can't work anymore.

Vue: Check form input is changed by user

I am new to Vue and want to achieve below result.
I have a form of data and a save button, before the page loads, it will fetch database and fill the form data. Because all the form data are filled, the save button is disabled and the user can not click unless the user change some data, then it knows the form data has changed, the save button will no longer be disabled and can be saved.
I know that should use watch property, but actually how I can implement this?
Thank you guys!
The form data is like this
data(){
return {
form: {
firstName: "",
lastName: ""
}
}
}
You can do something as below by having the two different objects actual and modified.
Here I have used underscore js for deep clone or isEqual, so don't forget to import.
computed: {
// Use is property for the save button to enable or disable
isDataChanged: function() {
return _.isEqual(this.actualForm, this.modifiedForm);
}
},
data() {
return {
actualForm: {
firstName: "",
lastName: ""
},
modifiedForm: {
firstName: "",
lastName: ""
}
}
},
methods: {
fetchData: function() {
// get data and assign it to actual form
this.actualForm = responseData;
this.modifiedForm = _.cloneDeep(this.actualForm) // this will create the new instance
}
}
You can use a watch on form like this below. You can also use deep:true if you need to watch nested property within form.
watch: {
form: {
deep: true,
handler(val) {
// Enable save button here. You can also evaluate any other condition to enable
}
}
}

Showing error for multiple empty inputs vuejs vuetify

I have three inputs as externalIP as External Host, externalHttpPort as Port and vendor as Vendor, I want to show error message if any of them is missing. but being specific about the name,
if (!this.externalIP || !this.externalHttpPort || !this.vendor) {
when I do if condition, this works but I am not sure how I can tell if which input is empty. only one way which left is to add 3 multiple if
something like this
let error;
error = !this.externalIP
? "External IP Address (or URL) is incorrect"
: "" || !this.externalHttpPort
? "Port is missing" : "" || !this.vendor
? "Please choose camera vendor or add your camera snapshot URL." : ""
if (!error == "") {
this.setNotification({
text: error,
color: "warning",
snackbar: true
})
return
}
is there any other way of doing it? Also, Eslint show warning on indentations as well
In this way, you can achieve the same result as by your logic without using multiple if. Also, this way will keep your template & logic clean/easily scalable.
new Vue({
el: '#app',
data: {
fields: [
{
key: 'externalIP',
value: '',
validationMessage: 'External IP Address (or URL) is incorrect'
},
{
key: 'externalHttpPort',
value: '',
validationMessage: 'Port is missing'
},
{
key: 'vendor',
value: '',
validationMessage: 'Please choose camera vendor or add your camera snapshot URL.'
}
]
},
computed: {
getError () {
let e = ''
for (let i in this.fields) {
if (!this.fields[i].value) {
e = this.fields[i].validationMessage
break
}
}
return e
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input
v-for="(field, i) in fields"
:key="i"
v-model="fields[i].value"
/>
<br>{{ getError }}
</div>
Well, you may assign an error field variable within your error-checking condition.
Example for three variables and an emptyField variable denoting the empty variable name:
if ((!a && (emptyField = "a")) ||
(!b && (emptyField = "b")) ||
(!c && (emptyField = "c"))) {
alert("Field " + emptyField + " is empty...");
}
Since JS lazily evaluates its arguments, emptyField = "a" will get executed only if !a is true.
Just note that lazy evaluation also causes the evaluation of conditions stop once a condition was hit, thus if a is null and also b is null, the alert would announce that "Field a is empty..."
if vuetify you can use rules to check inputs
for example:
in your template:
<v-text-field
rules="[rules.required]"
/>
and in your data:
data() {
return {
rules: {
required: (value) => !!value || 'This field is required.'
}
}
},
you can add any other checking to rules array and just add it to your input by passing it to rules prop
hope it helps you!!!
I'm using VeeValidate to handle all of Validation job, maybe you will be interested in it:
https://logaretm.github.io/vee-validate/

Update form data based on a method in VueJS

I am using VueJS and I have a form with two fields. The user is required to enter the first field, and I want the value of second field to be calculated using the value from first field and passing it through a method.
HTML
<div id="app">
<input type="text" v-model="value.label">
<input type="text" v-model="value.slug">
<!-- Slug value to display here -->
<br /><br />
{{value}}
</div>
Javascript
new Vue({
el:'#app',
data:{
value:{
label: '',
slug: '' // This value is calculated by passing label to santiize()
}
},
computed: {
},
methods: {
sanitize (label) {
return label+'something'
}
}
});
The user enters the first field which updates value.label
We need to pass value.label through sanitize() method and update value.slug . This should also immediately show up in the form field.I don't know how to do it. So, if the user types nothing in the form field it will have an automatic value returned as described.
Along with that it would have been awesome, if we allow the user to bypass what the santize function returns, if the user decides to type the slug value himself in the form field. So, if the user decides to type, the typed value will be set.
I created this fiddle for it - https://jsfiddle.net/9z61hvx0/8/
I was able to solve the problem by changing the data structure a bit and adding a watcher to 'label...
new Vue({
el:'#app',
data:{
label:'',
slug: ''
},
computed: {
computedSlug () {
return this.value.label+'Manish'
}
},
watch: {
label: function () {
this.slug = this.sanitize(this.label)
}
},
methods: {
sanitize (label) {
return label+'something'
}
}
});
Any other more sophesticated answers are most welcome :)

Categories