VueJS can't update v-text-field value dynamically - vue.js

I dynamically draw form input component (as in the image) using this code:
In this case the key can be "name","gruppo","codice" and so on.
<v-row>
<v-col v-for="(key,i) in keys_visible" :key="key" v-if="headers_visible[i].visible == true" cols="12" sm="12" md="12"
v-if="!(headers_visible[i].type == 'bit' && editedItem[key] == -9)">
<v-text-field #change="comp_change(key)" v-else-if="headers_visible[i].type == 'varchar'" v-model="editedItem[key]" :label="headers_visible[i].text"></v-text-field>
</v-col>
</v-row>
Then I have comp_change function which is defined in methods block:
comp_change (par1) {
var self = this;
self.editedItem["name"] = "example text";
},
I have placed a debugger; at the beginning of comp_change function, and it stops everytime so the function is triggered, but without displaying new value in "Nome" field (which v-model is editedItem["name"]). Why after comp_change I can't see "example text" in the field?
The form is already opened when I fire change

This is likely a reactivity issue. You should read up on this here. Also, if you use v-model, you do not need to set the value yourself, meaning you can do away with the #change call. You have two options as I see it.
a. Use root data objects on your component instead of an array/object and then use v-model as normal. This looks like:
<template>
<v-text-field v-model="name" />
<v-text-field v-model="email" />
</template>
<script>
export default {
data() {
return {
name: '',
email: '',
etc: ''
}
}
}
</script>
Now, when your form fields are updated by the user, you won't need to use #change to set the value. It will happen automatically.
b. Or, set the model with Vue.set(). In this case, you are not going to use v-model. Instead, you have defined your own methods to manage the data. This looks like:
<template>
<v-text-field #change="comp_change(key)" />
</template>
<script>
import Vue from 'vue';
export default {
data() {
return {
editedItem: {}
}
},
methods: {
comp_change (par1) {
Vue.set( this.editedItem, 'name', 'example text' );
}
}
}
</script>

Related

Vuex variable properly updated but not refreshed in template

When I change the value of my v-text-field, I can see from the Vue tab of the developer tools that the value of lastName is updated in the user store with my code below. But, for some reason, the values of my 3 tests in my template below are not updated (or synchronized). Why ? What's wrong in my code ?
pages/home.vue:
<template>
<v-container>
<v-container>
<v-row>
<v-col>
<v-text-field v-model="lastName" />
</v-col>
</v-row>
</v-container>
TEST 1:
{{ $store.state.user.last_name }}
TEST 2:
{{ lastName }}
TEST 3:
<v-text-field v-model="lastName" />
</v-container>
</template>
<script>
export default {
computed: {
lastName: {
get() {
return this.$store.state.user.last_name
},
set(value) {
this.$store.commit('user/UPDATE_PROP', ['last_name', value])
}
}
}
}
</script>
store/user.js
export const mutations = {
UPDATE_PROP(state, payload) {
state[payload[0]] = payload[1]
}
}
I think the problem is in the way you set up your store. According to the Nuxt documentation,
you create a store directory and
every .js file inside the store directory is transformed as a namespaced module (index being the root module).
Also, according to the documentation,
... your state value should always be a function to avoid unwanted shared state on the server side.
Your state should look something like this
store/user.js
export const state = () => ({
last_name: ''
})
export const mutations = {
UPDATE_PROP(state, payload) {
state[payload[0]] = payload[1]
}
}

Make Vuetify v-text-field component required by default

Currently, you can set rules that will work once the user changes the value of the input. For example:
Template part
<v-text-field
v-model="title"
label="Title"
></v-text-field>
Logic
export default {
data () {
return {
title: '',
email: '',
rules: {
required: value => !!value || 'Required.'
},
}
}
}
When the user focuses and removes focus from that element, or when the user deletes all its content, the required rule is triggered.
But what happens if we want to start with the rule enabled as soon as the component is mounted or created? Is there a way to achieve this?
I searched around vuetify but I could not find info about this nor examples in my humble google searches. I will appreciate help. I'm new in vue world. Thanks.
You could do the following:
Create a v-form and place your textfields inside the form. Don't forget to give your v-form a v-model and a ref too.
On mounted you can access the v-form via this.$refs and call .validate() just as Jesper described in his answer. In the codesandbox below you can see that the textfields immediately go red and display the "Required." text.
<v-form v-model="formValid" ref="myForm">
<v-text-field label="Field 1" :rules="rules.required"></v-text-field>
<v-text-field label="Field 2" :rules="rules.required"></v-text-field>
</v-form>
<script>
export default {
data() {
return {
formValid: false,
rules: {
required: [value => !!value || "Required."]
}
};
},
mounted() {
this.$refs.myForm.validate();
}
};
</script>
Example:
You should change your validation a little bit to achieve this.
<ValidationProvider rules="required" v-slot="{ errors }" ref="title">
<v-text-field
v-model="title"
label="Title"
></v-text-field>
</ValidationProvider>
And then you should call this.$refs.title.validate()
If you trigger this when mounted() is called, it should validate all the fields right away, as you're requesting.

How to use another method's variable in a vue component?

I have two methods in a vue component.
First makes the user choose from a v-select, either itemone or itemtwo. Then, to retreive the value for later i call #change to assign the variable to a method declared later - getItemValue.
Second is a submit button, when clicked, we go to handleSubmit.
After handleSubmit is called, I want to use the value I got from getItemValue (in variable theItem), but how can I call another method if it's out of my scope?
Mycomponent.vue
<template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-select
v-model="select"
:items="items"
#change="getItemValue"
></v-select>
<v-btn
#click="handleSubmit"
>
Submit
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
items: [
'itemone',
'itemtwo'
],
}),
methods: {
getItemValue(theItem) {
},
handleSubmit(e) {
e.preventDefault()
// i need "theItem" here!
}
},
}
</script>
v-model already writes to your local variable, so there is absolutely no need to setup a get method to write the select value to a variable.
Actually, v-model is a bit more complicated than just 'write' to a variable, but the important bit is that in your template you are setting up v-model="select", which basically means that whenever the user uses the select to pick a value, your local select variable will be updated with the selected value.
Now, there is no select in your example component data, I don't know why. But if you had it, you could just sent that variable in your handleSubmit:
<template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-select
v-model="select"
:items="items"
></v-select>
<v-btn
#click="handleSubmit"
>
Submit
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
select: '',
items: [
'itemone',
'itemtwo'
],
}),
methods: {
handleSubmit(e) {
e.preventDefault()
doSomethingWith(this.select); // this will be updated at this point
// with the option the user selected
}
},
}
</script>
Now, however, be aware that if the select variable is a component prop, then you should not do this right away, since props are not intended to be modified directly by child components. If that would be the case, please update your question with more info.
You would simple set the variable (theItem) value to the data
getItemValue(theItem) {
this.theItem;
},
and then retrieve it later
handleSubmit(e) {
e.preventDefault()
// i need "theItem" here!
// simple access theItem
console.log('theItem', this.theItem);
}

vuetify v-select options from json array of objects

I am trying to populate a vuetify v-select from a rest api json response. Below is the code I have so far. Populating the list using a simple array is no problem but I am having difficutly setting both the value and text properties.
<template>
<v-container fluid>
<v-slide-y-transition mode="out-in">
<v-layout column align-center>
<v-select v-model="dbSelect" v-on:change="dbConnect()" :items="dbOptions" single-line></v-select>
</v-layout>
</v-slide-y-transition>
</v-container>
</template>
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
data () {
return {
dbSelect: '',
dbOptions: [],
}
},
mounted () {
axios.get('http://localhost:5000/api/values')
.then(r => {
// var formatted = []
for (let i = 0; i < r.data.length; i++) {
this.dbOptions.push(r.data[i])
// formatted[i] = { value: r.data[i].id, text: r.data[i].name }
}
},
error => {
console.error(error)
}
)
}
}
</script>
This is the simple Json dataset:
[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"C"},{"id":4,"name":"D"},{"id":5,"name":"E"}]
Currently I am pushing in the whole object because I have been trying to use item-value and item-text but according to documentation I can see that is not right. r.data[i].id and r.data[i].name produce a drop-down list as expected. I want to set dbSelect to the value of the selection on change for a secondary call after selection.
Many thanks all for your help.
I had the same issue and felt this needed to be added as the correct answer that solved the issue.
Using the properties of item-text="" and item-value=""
<v-select
:items="dbOptions"
v-model="dbSelect"
item-text="name"
item-value="id"
></v-select>
The array of objects using name and id
[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"C"},{"id":4,"name":"D"},{"id":5,"name":"E"}]
See Example
This was originally answered by #Bert in the comments.

How to properly pass data from child to parent and parent to child component?

First of all, this is my current structure
CHILD COMPONENT
// HTML
<v-select
v-bind:items="selectItems"
v-model="selectedItem"
label="Category"
item-value="text"
></v-select>
<v-text-field
label="Enter Value"
type="number"
v-model="compVal"
></v-text-field>
// JS
props: {
selectItems: {
type: Array,
required: true
},
selectedItem: {
type: String
},
compVal: {
type: Number
},
}
PARENT COMPONENT
// HTML
<component :selecItems="selectOneItems" :selectedItem="selectOneItem"
:compVal="compOneVal"></component>
<component :selecItems="selectTwoItems" :selectedItem="selectTwoItem"
:compVal="compTwoVal"></component>
// JS
data () {
return {
selectOneItems: [someArray],
selectedOneItem: null,
compOneVal: 0,
selectTwoItems: [someArray],
selectedTwoItem: null,
compTwoVal: 0
}
}
My Scenario
1) At initial state, i have to get value from Child's Text Input and store it in local. (Child to Parent)
2) After browser refresh, i will check for any local data, if it exsist, i have to populate child's value (Parent to Child)
The error with current structure
whenever i type or select something it triggers this error
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "compOneValue"
so how to avoid this error? how to properly implement this structure, so that i can reuse this component wherever i want.
You are using props with v-model. Props will be overridden when the parent component is re-renders.
Use data with v-model and set the initial values from the props:
CHILD COMPONENT
// HTML
<v-select
v-bind:items="selectItems"
v-model="selectedItemModel"
label="Category"
item-value="text"
></v-select>
<v-text-field
label="Enter Value"
type="number"
v-model="compValModel"
></v-text-field>
// JS
props: {
selectItems: {
type: Array,
required: true
},
selectedItem: {
type: String
},
compVal: {
type: Number
},
},
data: function() {
return {
selectedItemModel: this.selectedItem,
compValModel: this.compVal
}
}
It might be worth changing your props to be called initialSelectedItem and initialCompVal instead, and then have the data properties called selectedItem and compVal.
You could use a locally bound setter then pass it to the child. The child has then a method to set property of the parent.
Here's an example.
<!-- Component.vue -->
<template>
<div class="login">
{{ property }}
<button v-on:click="setProperty('Something')">
</button>
<router-view/>
</div>
</template>
<script>
const setProperty = (propertyValue) => {
this.property = propertyValue
}
export default {
name: 'Login',
data () {
return {
// The 'bind(this)' is crucial since otherwise
// 'this' will refer to the context of the caller
// instead of what it is in this context.
setProperty: setProperty.bind(this),
property: 'Not something'
}
}
}
</script>