Updating Variable on Screen using vue - vue.js

I'm not quite sure what I'm missing here. In my vue project I would like to update a variable (pressed) on screen. I'm able to update the second value (username) by passing v-model="username" on the input field.
when adding {{ pressed ? 'true' : 'false' }} to the template I'm only ever able to return the set value from the data() section.
If someone is able to explain the concept, I'm very thankful. I tried looking up the issue in the docs but I'm unable find an explaination.
the Code I'm working on:
<template>
<div>
<div class="form-floating mt-4 mb-2">
<input type="text" v-model="username" class="form-control"
id="inputUsername" placeholder="username_idea">
<label for="inputUsername">Your Username</label>
</div>
<button #click="validate" :disabled="!username"
type="button" id="search" class="btn btn-primary btn-lg">
<VueFeather type="search"></VueFeather>
validate
{{ username == '' ? '#username ' : '#' + username }}
</button>
</div>
</template>
<script>
let username = ''
let pressed = false
export default {
data() {
return{
username: '',
pressed: false
}
},
methods: {
validate: () => {
console.log('fn validate called')
this.pressed = true
return{
pressed: this.pressed
}
},
},
}
</script>
I get 2 Console Results:
[Vue warn]: Unhandled error during execution of native event handler
at <Search>
at <HomeView onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< Proxy {…} > >
at <RouterView>
at <App>
warn # runtime-core.esm-bundler.js?d2dd:38
logError # runtime-core.esm-bundler.js?d2dd:212
handleError # runtime-core.esm-bundler.js?d2dd:204
callWithErrorHandling # runtime-core.esm-bundler.js?d2dd:158
callWithAsyncErrorHandling # runtime-core.esm-bundler.js?d2dd:164
invoker # runtime-dom.esm-bundler.js?2725:366
runtime-core.esm-bundler.js?d2dd:218
Uncaught TypeError: Cannot set properties of undefined (setting 'pressed')
at Proxy.validate (Search.vue?8555:51:1)
at onClick._cache.<computed>._cache.<computed> (Search.vue?8555:10:1)
at callWithErrorHandling (runtime-core.esm-bundler.js?d2dd:155:1)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js?d2dd:164:1)
at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js?2725:366:1)

Use regular functions instead of arrow functions in Vue methods() lifecycle. This is because an arrow function defines its own 'this' value. See the code below:
<template>
<div>
<div class="form-floating mt-4 mb-2">
<input type="text" v-model="username" class="form-control"
id="inputUsername" placeholder="username_idea">
<label for="inputUsername">Your Username</label>
</div>
<button #click="validate" :disabled="!username"
type="button" id="search" class="btn btn-primary btn-lg">
<VueFeather type="search"></VueFeather>
validate
{{ username == '' ? '#username ' : '#' + username }}
</button>
</div>
</template>
<script>
export default {
data() {
return{
username: '',
pressed: false
}
},
methods: {
validate() {
console.log('fn validate called')
this.pressed = true
return{
pressed: this.pressed
}
},
},
}
</script>

Related

Vue 3 Vuelidate not working with component

I am using vue 3 / typescript . I have 2 forms one is for patient registration and other is for patient visits both forms required validation on some fields. I am using vuelidate to validate both forms. I am have applied validation on both forms patient and visit . patient form is a component and i injected it inside the visit page .The problem is when i add patient component inside visit page vailidation on visit page get fails. but then i comment out the patient compoment on visit page it works fine. both forms are independent form each other.
Here is the patient registration component
<template>
<Dialog
id="previewReceiptDailog"
v-model:visible="productDialog"
:style="{ width: '60vw' }"
position="top"
class="p-fluid"
:modal="true"
:closable="true"
#hide="closeDialog"
>
<template #header>
<h6 class="p-dialog-titlebar p-dialog-titlebar-icon">
<i class="pi pi-plus-circle"></i> {{ dialogTitle }}
</h6>
</template>
<div class="p-grid">
<div class="p-col-6">
<div class="p-field">
<label
for="firstName"
:class="{
'p-error': v$.firstName.$invalid && submitted
}"
>First Name</label
>
<InputText
id="firstName"
v-model="v$.firstName.$model"
:class="{
'p-invalid': v$.firstName.$invalid && submitted
}"
/>
<small
v-if="
(v$.firstName.$invalid && submitted) ||
v$.firstName.$pending.$response
"
class="p-error"
>{{
v$.firstName.required.$message.replace(
"Value",
"First Name"
)
}}</small
>
</div>
</div>
</div>
<template #footer>
<Button
type="button"
label="Save"
icon="pi pi-check-circle"
class="p-button-primary pull-left"
#click.prevent="saveItem(!v$.$invalid)"
/>
</template>
</Dialog>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { reactive } from "vue";
import { required } from "#vuelidate/validators";
import useVuelidate from "#vuelidate/core";
#Options({
props: {
PatientRegistration: Object
},
watch: {
PatientRegistration(obj) {
this.openDialog();
this.dialogTitle = obj.dialogTitle;
this.productDialog = obj.status;
if (obj.patientID != 0) {
this.loadPatient(obj.patientID);
}
}
},
emits: ["updatePatientStatus"]
})
export default class PatientDialog extends Vue {
private submitted = false;
private state = reactive({
firstName: "",
});
private validationRules = {
firstName: {
required
},
};
private v$ = useVuelidate(this.validationRules, this.state);
//DEFAULT METHOD OF TYPE SCRIPT
//CALLING WHENEVER COMPONENT LOADS
created() {
this.toast = new Toaster();
this.patientService = new PatientService();
}
saveItem(isFormValid) {
this.submitted = true;
if (isFormValid) {
//DO SOMETHING
}
}
}
</script>
Here is the visit page where i injeceted the component of patient
<template>
<section>
<div class="app-container">
<Dialog
v-model:visible="productDialog"
:style="{ width: '50vw' }"
:maximizable="true"
position="top"
class="p-fluid"
>
<template #header>
<h5 class="p-dialog-titlebar p-dialog-titlebar-icon">
{{ dialogTitle }}
</h5>
</template>
<form #submit.prevent="saveVisitItem(!v1$.$invalid)">
<div class="p-field">
<label for="notes">Notes</label>
<InputText
id="notes"
v-model="v1$.notes.$model"
:class="{
'p-invalid': v1$.notes.$invalid && submitted
}"
/>
<small
v-if="
(v1$.notes.$invalid && submitted) ||
v1$.notes.$pending.$response
"
class="p-error"
>{{
v1$.notes.required.$message.replace(
"Value",
"Notes"
)
}}</small
>
</div>
<Button
type="submit"
label="Save"
icon="pi pi-check"
class="p-button-primary"
/>
</form>
</Dialog>
</div>
</section>
<PatientDialog
:PatientRegistration="{
patientID: this.patientID,
status: this.patientDialogStatus,
dialogTitle: this.dialogTitle
}"
v-on:updatePatientStatus="updatePatientStatus"
/>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import VisitService from "../../service/VisitService.js";
import { reactive } from "vue";
import useVuelidate from "#vuelidate/core";
import { required } from "#vuelidate/validators";
import PatientDialog from "../../components/PatientDialog.vue";
import AutoComplete from "primevue/autocomplete";
#Options({
components: {
PatientDialog,
AutoComplete
}
})
export default class EncounterVisit extends Vue {
private validationRules = {
notes: {
required
}
};
private visitFormState = reactive({
notes: "",
});
private v1$ = useVuelidate(this.validationRules, this.visitFormState);
//ADD OR UPDATE THE ITEM VIA HTTP
saveVisitItem(isFormValid) {
this.submitted = true;
console.log(this.v1$);
console.log(isFormValid);
//this.submitted = false;
}
}
</script>
The response i get from the console log is
$anyDirty: true
$clearExternalResults: ƒ $clearExternalResults()
$dirty: false
$error: false
$errors: []
$getResultsForChild: ƒ $getResultsForChild(key)
$invalid: true
$model: null
$path: "__root"
$pending: false
$reset: () => {…}
$silentErrors: (5) [Proxy, Proxy, Proxy, Proxy, Proxy]
$touch: () => {…}
$validate: ƒ ()
notes: Proxy {$dirty: ComputedRefImpl, $path: 'notes', required: {…}, $touch: ƒ, $reset: ƒ, …}
_vuelidate_72: Proxy {$dirty: ComputedRefImpl, $path: '__root', $model: null, $touch: ƒ, $reset: ƒ, …}
[[Prototype]]: Object
false

How to validate on input event with vee validate 3

I'm trying to make validation on input event, but the problem that when the event onInput fire in console.log(valid) - is not correct value because it validates the previos character, how to validate correcty on input event?
question.vue
<template>
<div>
<ValidationProvider v-slot="{ errors, passed }" name="question" rules="required|max:10000">
<e-form-group :error="errors[0]">
<template v-slot:label>
Question Text
</template>
<e-textarea
v-model="question"
name="question"
size="small"
#input="onInput($event, passed, name)"
></e-textarea>
</e-form-group>
</ValidationProvider>
<ValidationProvider v-slot="{ errors, passed }" name="button" rules="required|max:10000">
<e-form-group :error="errors[0]">
<template v-slot:label>
Button Text
</template>
<e-textarea v-model="button" name="button" size="small" #input="onInput($event, passed, name)"></e-textarea>
</e-form-group>
</ValidationProvider>
</div>
</template>
<script>
export default {
name: 'Question',
props: {
questionId: {
type: Number
}
},
data: () => ({
question: '',
button: '',
}),
methods: {
onInput(event, valid, name) {
console.log(event);
console.log(valid);
if (!valid) this.$emit({questionId: this.questionId, name: name})
},
},
};
</script>
The default is to validate on input already, so what you want to do is trigger the validation yourself, get the result, and then emit your event:
<ValidationProvider v-slot="{ errors, validate }" name="question" rules="required|max:10000">
<e-form-group :error="errors[0]">
<template v-slot:label>
Question Text
</template>
<e-textarea
v-model="question"
name="question"
size="small"
#input="onInput($event, validate, name)"
></e-textarea>
</e-form-group>
</ValidationProvider>
methods: {
onInput(event, validate, name) {
var self = this;
validate(event).then(function(result) {
if (result.valid) self.$emit({questionId: self.questionId, name: name})
});
},
},

vform multiselected dropdown default selected and expected an array error

Can anyone help me I used Vform When I clicked edit button then it's show this error that expect array but I used already array in categories[] and also how to selected default category ?
I used vform and axios. I want to make a post with multiple category it's store successfully but when I click edit button then I get error that it's expected an array but I bind it an array and also how I make default select when it's edit mode ?
<form #submit.prevent="editMode ? updateVal() : createVal()">
<div class="modal-body">
<div class="form-group">
<label>Category</label>
<select class="form-control"
multiple
v-model="form.categories"
name="categories[]"
:class="{ 'is-invalid': form.errors.has('categories') }">
<option v-for="catetoryValue in getCategory" :key="catetoryValue.id" :value="catetoryValue.id">{{ catetoryValue.name }}</option>
</select>
<has-error :form="form" field="categories"></has-error>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button v-show="editMode" type="submit" class="btn btn-primary">Update</button>
<button v-show="!editMode" type="submit" class="btn btn-primary">Create</button>
</div>
</form>
<script>
export default {
data() {
return {
editMode: false,
categories:[],
selected: null,
form: new Form({
id: "",
title: "",
categories:[],
})
};
},
methods: {
getCategory(){
if (this.$gate.isAdminOrAuthor()) {
axios
.get("api/getcategory")
.then((response) => {
this.getCategory = response.data
})
.catch(() => {
console.log("Error...");
});
}
},
newModal() {
this.editMode = false;
this.form.reset();
$("#addnewmodal").modal("show");
},
edidModal(value) {
this.editMode = true;
this.form.reset();
$("#addnewmodal").modal("show");
this.form.fill(value);
},
},
created() {
this.getCategory();
},
};
</script>

v-if not working for form validation and displaying errors

I'm trying to validate a simple form that contains two fields:
A select box
A file field
If one of the fields aren't filled in, a div (containing an error label) should be rendered next to the corresponding input field.
The problem: My 'error divs' aren't rendered when pushing data to the errors object (if the form is invalid).
Please note my console.log statement, that tells me that my error object has a key 'file' and a key 'selectedSupplier'.
Side note: I'm following this example: https://v2.vuejs.org/v2/cookbook/form-validation.html
Differences are, that I'd like to show error labels next to the corresponding field and that I'm setting errors in my errors object, instead of a simple array. So what could I be doing wrong?
Thanks.
This is my Main.vue file:
<template>
<div>
<form #submit="upload">
<div class="mb-8">
<h1 class="mb-3 text-90 font-normal text-2xl">Import Order Csv</h1>
<div class="card">
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="supplier_id" class="inline-block text-80 pt-2 leading-tight">Supplier</label>
</div>
<div class="py-6 px-8 w-1/2">
<select v-model="selectedSupplier" id="supplier_id" name="supplier_id" ref="supplier_id" class="w-full form-control form-input form-input-bordered">
<option v-for="supplier in suppliers" v-bind:value="supplier.id">{{ supplier.name }}</option>
</select>
<div v-if="errors.hasOwnProperty('selectedSupplier')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="csv_file" class="inline-block text-80 pt-2 leading-tight">File</label>
</div>
<div class="py-6 px-8 w-1/2">
<input id="csv_file" type="file" name="file" ref="file" #change="handleFile">
<div v-if="errors.hasOwnProperty('file')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center">
<button type="submit" class="btn btn-default btn-primary inline-flex items-center relative">Import</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
this.listSuppliers();
},
data() {
return {
errors: [],
file: '',
suppliers: [],
};
},
methods: {
checkForm() {
if (!this.selectedSupplier) {
this.errors.selectedSupplier = 'Supplier required';
}
if (!this.file) {
this.errors.file = 'File required';
}
},
listSuppliers() {
const self = this;
Nova.request()
.get('/tool/import-order-csv/suppliers')
.then(function (response) {
self.suppliers = response.data.data;
})
.catch(function (e) {
self.$toasted.show(e, {type: "error"});
});
},
handleFile: function (event) {
this.file = this.$refs.file.files[0];
},
upload: function (event) {
this.checkForm();
if (this.errors.hasOwnProperty('selectedSupplier') || this.errors.hasOwnProperty('file')) {
console.log(this.errors); // this actually shows both errors!
event.preventDefault();
}
const formData = new FormData();
formData.append('file', this.file);
formData.append('supplier_id', this.$refs.supplier_id.value);
const self = this;
Nova.request()
.post('/tool/import-order-csv/upload',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function (response) {
self.$toasted.show(response.data.message, {type: "success"});
})
.catch(function (e) {
self.$toasted.show(e.response.data.message, {type: "error"});
});
}
}
}
</script>
Apparently I had to use v-show instead of v-if, because v-if would be 'lazy' and will not render my error-div when the errors var gets filled.
It's working now, but not 100% sure if this is the best way, as I found another tutorial where v-if is used for form validation.(https://medium.com/#mscherrenberg/laravel-5-6-vue-js-simple-form-submission-using-components-92b6d5fd4434)
I was getting the same error, this is how I solved the problem,
<div v-if="errors.field1.length > 0 ? true : false"> // true or false
If you fix the code like this it will work
The reason might because the way you reassign object is not reactive, which not trigger v-if to re-calculate
this.errors.selectedSupplier = 'Supplier required';
this.errors.file = 'File required';
If you still want to use v-if , try change to this approach
this.errors = {...this.errors, selectedSupplier: 'Supplier required' }
this.errors = {...this.errors, file: 'File required' }
The way I handle my errors with VueJS is through lists and their length attribute.
I have an errors object in my data that looks like this:
errors: {
field1: [],
field2: [],
}
Then, when I submit the form, I will:
Empty all the lists for the errors (ie clearing the previous errors)
.push() new errors in the right lists (and .push() makes the Vue reactive)
Finally, in my form, my respective errors divs are displayed based on the length of the list:
<div class="error" v-if="errors.field1.length > 0">
use a v-for to display all the errors from the list
</div>
Hope it helps

How can I abstract vee-validate errors properties?

I currently have a custom component with v-validate on the parent. I would like to abstract out the "fields.email && fields.email.touched ? errors.first('email') : ''" into it's own method, preferably on a mixin. I have tried creating a method that takes in a name and returns the result of the above, this does not seem to work due to methods not being reactive. I have also tried creating a computed property, but cannot seem to make one that is dynamic. A solution that may work is dynamically creating computed properties, however: i believe this is not possible.
Here is my code:
InputField.vue:
<template>
<label :for='name' :class="{error: error}">
{{ label }}
<input
class='input'
:value="value"
:disabled="disabled"
:name="name"
:type="type"
:placeholder="placeholder"
#input="$emit('input', $event.target.value)"
#change="$emit('change', $event.target.value)"
#blur="$emit('blur')"
/>
<p v-if="error">{{ error }}</p>
</label>
</template>
<script>
export default {
props: {
label: String,
name: String,
type: String,
value: [String, Number],
placeholder: [String, Number],
disabled: {
type: Boolean,
default: false
},
error: String
}
};
</script>
LoginModal.vue
<template>
<div class='login-modal-inner'>
<div class='left-box form-wrapper'>
<h3>Login</h3>
<form #submit.prevent="submitForm('login')">
<input-field
v-model.trim="login.email"
name='email'
label='Email'
type='email'
v-validate="'required|email'"
:error="fields.email && fields.email.touched ? errors.first('email') : ''"
/>
<input-field
v-model="login.password"
name='password'
label='Password'
type='password'
v-validate="'required'"
/>
<app-button class='submit'>Log In</app-button>
</form>
</div>
...
</div>
</template>
<script>
import InputField from "../UI/input/InputField.vue";
import Button from "../UI/input/Button.vue";
import InputValidationError from "../UI/input/Button.vue";
export default {
data() {
return {
register: {
email: "",
password: "",
confirmPassword: ""
},
login: {
email: "",
password: ""
}
};
},
methods: {
submitForm(type) {
this.submitClicked = true;
this.$validator.validate();
this.$emit(`${type}-submit`, this[type]);
},
},
components: {
InputField,
appButton: Button
}
};
</script>