Is there is a better way to capture erros from Vuetify components? - vue.js

I'm using the Vue.js library Vuetify to add a few text field components inside on a component that I create. In order to provide input validation I would like to capture the hasError property of the text field components. I know the path of the property is: this.$children[3]._computedWatchers.hasError.active. But I would like to know if there is another way to access theses properties. Maybe I'm missing something?
<template>
<div class="register">
<form>
<div>
<v-text-field name="new-user-email"
label="Email"
type="email"
single-line
required></v-text-field>
</div>
<div>
<v-text-field name="user-user-password"
label="Password"
type="password"
single-line
required>
</v-text-field>
</div>
<div>
<v-text-field name="new-user-password-confirmation"
label="Confirm Password"
type="password"
single-line
required>
</v-text-field>
</div>
<div #click="registerNewUser">
<v-btn>Register</v-btn>
</div>
</form>
</div>
</template>
<script>
export default {
name: 'register-new-user',
data() {
return {
checked: false
};
},
methods: {
registerNewUser() {
console.log(this.$children[3]._computedWatchers.hasError.active)
console.log('Register a new user')
}
}
};
</script>

Add a ref attribute to the v-text-field component like this:
<v-text-field
ref="password-confirmation"
name="new-user-password-confirmation"
label="Confirm Password"
type="password"
single-line
required
></v-text-field>
Then you can reference the VueComponent instance of the Vuetify text field component (along with its properties and methods) like so:
methods: {
registerNewUser() {
console.log(this.$refs['password-confirmation'].hasError)
}
}
Here's documentation on refs.

Related

Rookie Question about Vue with input and ref value. Display content of input only after click (not when typing)

I have an input and a click button.
When I click I request a database (async/await) and finally display a table with requested data.
At the same time I display the input keyword like "{{input}} was found X times".
But then I want to do another research but the text of input is reactive and change while I'am typing. I would like to only display the new keyword input after the click.
I'm struggling to do that.
<div class="q-pa-md">
<div class="q-gutter-md" style="max-width:600px">
<q-input outlined bottom-slots v-model="gene" label="Gene Symbol :" >
<template v-slot:before>
<q-icon name="search" />
</template>
<template v-slot:hint>
Ligand or Receptor
</template>
<template v-slot:after>
<q-btn #click="search" label="Search :" />
</template>
</q-input>
</div>
Elsewhere I have this for the display:
<div class="text-h6">
<q-badge align="middle" style="background-color:#FF4F00;font-size:20px;padding:12px">
Interactions > </q-badge> {{ gene }} was found XX times.
</div>
When you use v-model, the value changes with any input change. To avoid it you can define a new variable to store the gene value whenever q-btn is clicked; Like this:
<template>
<input v-model="gene" type="text">
<button #click="search">search</button>
<div v-if="searchedFor">
{{ searchedFor }} was found X times
</div>
</template>
<script>
export default {
data() {
return {
gene: "",
searchedFor: ""
}
},
methods: {
search() {
// Clear the previous one
this.searchedFor = ""
// API fetch ...
this.searchedFor = this.gene
}
}
}
</script>

Is it possible to use a prop as a v-model value?

Is it possible to use the value of a prop as the input's v-model?
I normally do the following when creating an input:
<template>
<form>
<input v-model="form.email" type="email"/>
</form>
</template>
<script>
export default {
data() {
return {
form: {
email: '',
}
}
}
}
</script>
But now I'm trying to achieve the following where this.myProp is used within the v-model without being displayed as a string on the input:
<template>
<form>
<input v-model="this.myProp" type="email"/>
</form>
</template>
<script>
export default {
props: ['myProp'] // myProp = form.email for example (to be handled in a parent component)
}
</script>
Yes, but while using it in parent component. In child component you need to extract value and #input instead of using v-model (v-model is shortcut for value="" and #input) Here is an example of input with label, error and hint in Vue 3 composition API.
BaseInput.vue
<template>
<div class="flex flex-col">
<label>{{ label }}</label>
<input v-bind="$attrs" :placeholder="label" :value="modelValue" #input="$emit('update:modelValue', $event.target.value)">
<span v-for="item of errors" class="text-red-400">{{ item.value }}</span>
<span v-if="hint" class="text-sm">{{ hint }}</span>
</div>
</template>
<script setup>
defineProps({ label: String, modelValue: String | Number, errors: Array, hint: String })
defineEmits(['update:modelValue'])
</script>
Using v-bind="$attrs" you target where attributes like type="email" need to be applied in child component. If you don't do it, it will be added to the top level DOM element. In above scenario <div>.
ParentComponent.vue
<BaseInput type="email" v-model="formData.email" :label="Email" :errors="formErrors.email"/>

Why cannot I hide a div with v-if?

Scenario: I have two options for a signup page. In the second one, I have a few more input elements and I only want to render that div when I click the second checkbox. For that, I wrote a v-if and it does not work properly.
(the project is in Vue)
<v-checkbox v-model="checkboxes" label="Bireysel Üyelik" value="checkbox-1"></v-checkbox>
<v-checkbox v-model="checkboxes" label="Kurumsal Üyelik" value="checkbox-2"></v-checkbox>
<div v-if="checkboxes === 'checkbox2'">
<v-text-field
name="cellPhone"
label="Cep Telefonu"
type="text"
key="cell-phone"
outlined
dense
required
>
</v-text-field>
</div>
data() {
return {
checkboxes: "",
}
}
The Html and JS files are as above. The data is dynamically changed in the Vue instance and there is no problem with that. The problem is with the conditional rendering mentioned above.
In your checkbox you bind a value of checkbox-2
<v-checkbox v-model="checkboxes" label="Kurumsal Üyelik" value="checkbox-2"></v-checkbox>
but then you look for checkbox2
<div v-if="checkboxes === 'checkbox2'">
<v-text-field
name="cellPhone"
label="Cep Telefonu"
type="text"
key="cell-phone"
outlined
dense
required
>
</v-text-field>
change it to:
<div v-if="checkboxes === 'checkbox-2'">
<v-text-field
name="cellPhone"
label="Cep Telefonu"
type="text"
key="cell-phone"
outlined
dense
required
>
</v-text-field>
and it will work :) .
Hope this helps!

Vue.js BootstrapVue : Veevalidate cannot validate datepicker

In my Laravel+Vue.js SPA ( Single Page Application) I am using the datepicker package from here, BootstrapVue from here and Veevalidate from here .
I think I need to show only the code inside my component file as only the datepicker component causes the problem and not other ones. My code follows in EditProfile.vue:
<ValidationObserver ref="form" v-slot="{ passes }">
<div id="registration_form">
<b-form #submit.prevent="passes(onSubmit)" #reset="resetForm">
<ValidationProvider vid="name" rules="required|min:2" name="name" v-slot="{ valid, errors }">
<b-form-group
label="User Name:"
label-for="exampleInput1"
>
<b-form-input
type="text"
v-model="name"
:state="errors[0] ? false : (valid ? true : null)"
placeholder="Enter your name"
></b-form-input>
<b-form-invalid-feedback id="inputLiveFeedback">{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</ValidationProvider>
<ValidationProvider vid="dob" rules="required" name="dob" v-slot="{ valid, errors }">
<b-form-group
label="Date of Birth:"
label-for="exampleInput1"
>
<datepicker
type="text"
v-model="dob"
required
format="d-M-yyyy"
:state="errors[0] ? false : (valid ? true : null)"
>
</datepicker>
<b-form-invalid-feedback id="inputLiveFeedback">{{ errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</ValidationProvider>
<b-button type="submit" variant="primary">Submit</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
</div><!-- end of id registration_form-->
</ValidationObserver>
JS part inside EditProfile.vue is:
import Datepicker from 'vuejs-datepicker';
import { ValidationObserver, ValidationProvider } from "vee-validate";
export default {
name: "EditProfile",
components: {
ValidationObserver,
ValidationProvider,
Datepicker
},
data: () => ({
name: "",
dob:""
}),
methods: {
onSubmit() {
console.log("Form submitted yay!");
},
resetForm() {
this.name = "";
this.dob = "";
requestAnimationFrame(() => {
this.$refs.form.reset();
});
}
}
};
When the submit button is pressed then validation works for name field but no error message shows up when dob field on datepicker is empty .
My Vue.js version is 2.6.10 and I used the latest versions of BootstrapVue and Veevalidate (3) as well.
How can I make the validation work for the date picker as well ?

validateAll doesn't work with inputs generated by v-for

I've got a form in which the inputs are added dynamically with the v-for loop. Each field should be validated, and before user submit the form it should be checked wherever it's valid or not. The problem is that the this.$validator.validateAll() always return true, even if the inputs are invalid. What I'm doing wrong?
<div id="app">
<v-app id="inspire">
<v-flex md4 offset-md4>
<form data-vv-scope="photoForm">
<v-text-field v-for="index in 5"
:key="index"
:label="'photo' + index"
:error-messages="errors.collect('photoForm.photoName' + index)"
v-validate="'max:10'"
:data-vv-name="'photoForm.photoName' + index"
color="purple" autocomplete="on"
counter="10" >
</v-text-field>
</form>
<p>Is valid form? {{ validationResult }}</p>
</v-flex>
<v-btn #click="validate" color="purple" dark>
validate
</v-btn>
</v-app>
</div>
Vue.use(VeeValidate);
new Vue({
el: "#app",
data() {
return {
validationResult: ''
}
},
methods: {
validate() {
this.$validator.validateAll('photoForm').then(result => {
this.validationResult = result
})
}
}
});
And codepen where I reproduce the problem: https://codepen.io/anon/pen/jjrJdE
You need to store your form data somewhere so the validation has something to work on, I assume.
See https://codepen.io/cwg999/pen/MMjWNj?editors=1011
The main changes I made were to put your dynamically generated inputs into your data() and used that to reference them in the for-loop.
(note: you can also use v-model instead of :value/#input)
<v-text-field v-for="o,i in photoForm"
:key="i"
:label="o.label+ ' ' + (i+1)"
:error-messages="errors.collect('photoForm.photoName' + i)"
v-validate="'max:10'"
:name="'photoName' + i"
:value=o.value
#input="o.value = $event"
color="purple" autocomplete="on"
counter="10" >
</v-text-field>
data() {
return {
validationResult: '',
photoForm:[
{label:'Photo',value:''},
{label:'Photo',value:''}
]
}
},