v-select component from child to parent binding when editing - vue.js

I'm running again another issue on my code, that although I can save the v-select component. I have 2 of these, the another is good in which the type items are object. The problem is by clicking edit button and passing value from child component to parent component, when I console the issue in edit method, I can see the output in console but it does not display on the imported component which is claimsdropdown. And when I update the another v-select imported component named type, the claimsdropdown value is passing null, that although it has already value in it based on fetching from database.
Here is my v-select component stuff:
<template>
<v-select
:items="items"
item-text="text"
item-value="value"
v-model="selected"
return-object
:menu-props="{ bottom: true, offsetY: true }"
append-icon="fas fa-chevron-down"
placeholder="Claims"
outlined
dense
/>
</template>
<script>
import { mapState } from "vuex"
export default {
name: "ClaimsDropdown",
data: () => ({
items: [
{
text: "Yes",
value: true
},
{
text: "No",
value: false
}
]
}),
computed: {
...mapState({
selected: state => state.projects.selected
}),
selected: {
get() {
return this.$store.state.projects.selected;
},
set(item) {
this.$store.commit("projects/SET_SELECTED", {
selected: item.value
});
this.$store.commit("projects/SET_FILTER_IS_CLAIMED", {
is_claimed: item?.value
});
}
}
}
};
</script>
<style scoped></style>
Parent Component:
<v-row>
<v-col cols="4">
<ClaimsDropdown v-model="this.selected.claim" />
</v-col>
</v-row>
on my computed:
computed: {
...mapState({
selected: state => state.projects.selected
})
}
and on method edit:
this.selected.claim = getFilterPresetsData.filters.claimed;
console.log({checkingClaim: this.selected.claim}); //display whatever the value from database.
Vuex: projects >name of vuex store
state: selected: null
my brain got drain by this one.

Related

V-autocomplete not updating on user interaction

I am having 3 multi-select elements on a page and one button. On click on the button, I want whatever the value selected on the third i.e last multi-select element, shall get selected on all top two multi-select elements. On clicking on button, i do see the values are getting updated in vuex.state.vModelValues based on the given key, but the updated values are not reflecting on the first 2 multi-select.
Note: I need to get/set the value of the first 2 multi-select from a JSON object based on the key/value pair. I can use an individual array with v-model with first 2 multi-select element, but i don't want to go with that approach as i may have 100 multi-selects element and can't define 100 array in data section. So want to define a JSON which will hold the key/value for each multi-selects and should be reactive. If the value of one entry change, the key that mapped to multi-select shall get updated immediately.
I am not sure what i am doing wrong here. The value are updated on store but the updated value are not get selected on UI. Please help.
Below is my code:
1. STORE
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state() {
return {
vModelValues: {},
};
},
getters: {
getVModelPlaceholder(state) {
return state.vModelValues;
},
getVModelValue(state, key) {
return state.vModelValues[key];
},
},
mutations: {
setVModelValue(state, payload) {
state.vModelValues[payload.key] = payload.value;
},
},
actions: {
setVModelValue(context, payload) {
context.commit("setVModelValue", payload);
},
},
modules: {},
});
APP.VUE
<template>
<v-app>
<v-main>
<HelloWorld />
</v-main>
</v-app>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld,
},
data: () => ({}),
mounted: function () {
this.$store.dispatch("setVModelValue", {
key: "listbox_checkbox_ID1",
value: ["Three"],
});
this.$store.dispatch("setVModelValue", {
key: "listbox_checkbox_ID2",
value: ["Two"],
});
},
};
</script>
HelloWorld.vue
<template>
<v-container>
<div id="container_dummy_PID1" style="height: auto; width: 100%">
<v-autocomplete
id="listbox_checkbox_1"
:items="ranges"
v-model="$store.state.vModelValues['listbox_checkbox_ID1']"
hide-details
multiple
dense
return-object
cache-items
>
</v-autocomplete>
<v-autocomplete
id="listbox_checkbox_2"
:items="ranges"
v-model="$store.state.vModelValues['listbox_checkbox_ID2']"
hide-details
multiple
dense
return-object
cache-items
>
</v-autocomplete>
<v-autocomplete
v-model="selectedValues"
:items="ranges"
hide-details
multiple
dense
required
return-object
>
</v-autocomplete>
<v-btn color="primary" #click="applyToAll()">Apply to All</v-btn>
</div>
</v-container>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
selectedValues: "",
ranges: ["One", "Two", "Three", "Four", "Five"],
};
},
methods: {
applyToAll() {
this.$store.dispatch("setVModelValue", {
key: "listbox_checkbox_ID1",
value: this.selectedValues,
});
this.$store.dispatch("setVModelValue", {
key: "listbox_checkbox_ID2",
value: this.selectedValues,
});
},
},
};
</script>

Vue El-Select doesn't work as expected while passing value from parent to child using watch

Component A
<el-select v-model="value" placeholder="Select Action" #change="updateDropdowns($event)" prop="action">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
<tableview
:action="this.value"
v-show="false">
</tableview>
<script>
export default {
name: 'warehouseAdd',
components: {
tableview
},
props: [
'component'
],
data() {
return {
options: [
{
value: 'add',
label: 'Add'
}, {
value: 'edit',
label: 'Edit'
}
],
value: '',
Component B
<script>
props: [
'action'
],
watch: {
'action': function (value) {
console.log(value);
//Select value from dropdown doesn't pass the value at 1st attempt.
//If I again select the value from dropdown then I get the value.
}
}
Here whenever I try to get value.. I am unable to get value on first event. I need value once I select value from dropdown. I might be doing something wrong not exactly sure.. new to VueJs.
Please let me know how do I pass value at 1st instance to Component B.
Although I am getting the value in Component A on first attempt just not getting at Component B
I am able to fetch dropdown value in parent component but unable to fetch the same in child component when I initially select the value for 1st time. When I change the value of dropdown 2nd time then I get the value in child component.
Thanks!
In your question title you said that El-Select doesn't work as expected. Actually it works as expected. When you reload the page, all Vue data come back to their initial values. So when you set value: '' in component A (that I called it parentTable in my following codes), then after reloading the page the value of '' comes back to that value data. If you want to store the value of selected item before reloading page, there are some solutions to that. One of them is using Window localStorage. Here is the codes that works for me:
parentTable.vue component:
<template>
<div>
<el-select v-model="value" placeholder="Select Action">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
<tableview
:action="this.value"
v-show="false">
</tableview>
</div>
</template>
<script>
import tableview from "./tableview";
export default {
name: "parentTable",
components: {
tableview
},
data() {
return {
options: [
{
value: 'add',
label: 'Add'
}, {
value: 'edit',
label: 'Edit'
}
],
value: null,
}
},
watch: {
value: function (newVal) {
this.getValueOf(newVal);
}
},
methods: {
getValueOf: function (storeValue) {
localStorage.setItem("option", storeValue);
}
},
mounted() {
/* Here we get the previous selected item from localStorage */
this.value = localStorage.getItem("option");
}
}
</script>
<style scoped>
</style>
tableview.vue component:
<template>
</template>
<script>
export default {
name: "tableview",
props: [
'action'
],
watch: {
action: function (value) {
console.log(value);
}
}
}
</script>
<style scoped>
</style>

Reset select values in v-autocomplete, in order to add multiple items

I'm creating an application that has a selection box to choose between some template data. However, the user should be able to select the same template option several times and, each time he selects the template, a new informational box appears in the screen.
My problem is that the v-autocomplete component doesn't enable this kind of behavior: we can select one option (or multiple options), but not the same option twice.
I thought about making something like this: every time the user selects the option A, the infobox would appear below and the component would reset to a empty option. Then, the user could choose option A again and, when he chooses it, another info box would appear, how many times the user needs it.
How could I do something like this using vue? I didn't found any component that would do something like this on default, so I think I'll have to tweak the component behavior, but I don't know exactly where to start.
My template:
<template>
<div class="select-wrapper" id="selectBox">
<v-autocomplete
class="select-input"
:items="items"
:name="label"
placeholder="select item"
solo
:value="value"
#change="$event => onChange($event, items)"
item-text="name"
item-value="value"
:required="required"
:rules="[
value =>
!required ||
!!value ||
"required"
]"
></v-autocomplete>
</div>
</template>
And my Vue code:
<script lang="ts">
import { defineComponent } from "#vue/composition-api";
import Vue from "vue";
interface SelectItem {
name: string,
value: string
}
interface SelectBoxProps {
items: SelectItem[];
value: string;
onSelect: ({ target }: { target?: SelectItem }) => void;
hasResetSelection: boolean;
}
export default defineComponent({
name: "SelectBox",
props: {
label: String,
items: Array,
value: [String, Number],
onSelect: Function,
disabled: Boolean,
required: {
type: Boolean,
default: false
},
hasError: Boolean,
errorMessage: String,
hasResetSelection: {
type: Boolean,
default: false
}
},
directives: {
ClickOutside
},
setup({ onSelect, hasResetSelection }: SelectBoxProps) {
const onChange = (selectedValue: string, itemsArr: SelectItem[]) => {
const targetItem = itemsArr.find(i => i.value === selectedValue);
if (hasResetSelection) {
Vue.nextTick(() => {
console.log("onselect should reset value");
return onSelect({ target: { name: "", value: "" } });
});
}
return onSelect({ target: targetItem });
};
return {
onChange
};
}
});
</script>
This was my last attempt with Vue.nextTick, I already tried to tweak the component with ref() and it didn't work as well. Do you have any suggestions?
You can use another variable just to hold the input for the autocomplete component Like this:
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
items: [{ name : 'hello', value : 1 }, { name : 'world', value : 2 }],
value : null,
values : []
},
methods: {
onChange() {
this.values.push(this.value)
this.$nextTick(() => {
this.value = null
})
},
}
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-container>
Values : {{values}}
<v-autocomplete
:items="items"
placeholder="select item"
solo
v-model="value"
item-text="name"
item-value="value"
#change="onChange"
/>
</v-container>
</v-app>
</div>

Setting v-select value programmatically does not fire #change in vuetify

I am trying to set v-select value programmatically, however the associated #change event does not fire. If I manually select the option, the event fires as expected.
Template:
<div id="app">
<v-app dark>
<v-select
v-model="currentItem"
:items="items"
#change="itemChanged"
/>
</v-app>
</div>
JS:
Vue.use(Vuetify);
var vm = new Vue({
el: "#app",
methods: {
itemChanged(item) {
console.log('item changed',item);
}
},
data: {
currentItem: '',
items: [
{ text: "Name", value: 'name' },
{ text: "Number", value: 'number' },
{ text: "Inputs", value: 'inputs' }
]
},
mounted() {
setTimeout(() => {
this.currentItem = 'inputs';
}, 2000);
}
});
The jsfiddle is here
Does anybody have any solutions aside from manually calling the function when setting the value programmatically?
The change event is defined in such a way so it is emitted when the input is changed by user interaction (see the event documentation). If you want to monitor all the changes, you can either use the input event:
<v-select
v-model="currentItem"
:items="items"
#input="itemChanged"/>
https://jsfiddle.net/0qbomzta/
or add a watcher to watch the currentItem:
<div id="app">
<v-app dark>
<v-select
v-model="currentItem"
:items="items"/>
</v-app>
</div>
In Vue instance, add:
watch: {
currentItem (newVal) {
console.log('item changed', newVal);
}
},
https://jsfiddle.net/w5voub3h/

How to get the values from the DOM controls when the models are created dynamically using vuejs

I am creating a dynamic form generator component in which the DOM controls to be displayed are getting populated from an API service. The model key i.e. the :v-model is also getting added dynamically. I am not able to access the data which the user inputs in the DOM control.
The component from where all the data is passed to the form-generator
<template>
<form-generator :schema="schema" :model="model" :options="formOptions"></form-generator>
</template>
<script>
export default {
components: {
FormGenerator
},
data() {
return {
model: {},
schema: {
fields: [{"id":"dcno1","label":"DC No.","model":"dcno","name":"dcno","ref":"dcno","className":"","type":"input","inputType":"text","required":true,"order":2}]
}
}
}
</script>
In the above code, model and schema.fields are getting populated from an async axios API call.
If we pass value in the model for the model named "dcno", the value is getting filled in the input control
Dynamic Form Generator Component
<template>
<v-form v-if='schema!==null' v-model="model.valid" ref="form" lazy-validation>
<v-flex xs6 v-for='(formItem,index) in schema.fields' v-bind:key='index'>
<v-text-field :v-model='formItem.model' ref="formItem.model"
:label='formItem.label' :rules='formItem.rules'
:value='model[formItem.model]'
:type='formItem.inputType' :value='model[formItem.model]'
:id='formItem.id'>
</v-text-field>
</v-flex>
<v-btn class='float-right' color="primary" #click="submitForm">Submit</v-btn>
</v-form>
</template>
<script>
export default {
name: 'form-generator',
components: {
},
props: {
schema: Object,
model: Object
},
methods:{
submitForm: function(e) {
//how to access the form model values here
}
}
}
</script>
When passed a static value to model dcno, :value='model[formItem.model]', the value is getting displayed.
Consider the fields key has a set of controls.
Please help me in getting the values of the form in the submit function.
As you can see in the docs you cannot change the value of a component property, you need to create a support object inside the inner component and emit its value to the main component. Take a look at the example, i create innerModel based on the schema structure then i emit every changes to the innerModel by watching it.
Vue.config.devtools = false;
Vue.config.productionTip = false;
const formGenerator = Vue.component('form-generator', {
props: ['schema', 'value'],
data() {
return {
innerModel: [],
}
},
watch: {
schema: {
deep: true,
immediate: true,
handler() {
this.innerModel = this.schema.fields.map((field) => ({
// pass here any other property you need
name: field.name,
value: field.model,
}));
},
},
innerModel: {
deep: true,
immediate: true,
handler(value) {
this.$emit('input', value);
},
}
},
methods: {
submitForm: function(e) {
e.preventDefault();
// convert innerModel into an object if needed
console.log(this.innerModel);
}
},
template: `
<form #submit="submitForm">
<input
:key="model.name"
v-model="model.value"
v-for="model in innerModel">
<button type="submit">SUBMIT</button>
</form>
`
})
new Vue({
el: '#app',
components: {
formGenerator
},
data: {
model: {},
schema: {
fields: [{
id: "dcno1",
label: "DC No.",
model: "dcno",
name: "dcno",
ref: "dcno",
className: "",
type: "input",
inputType: "text",
required: true,
order: 2
}]
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<form-generator :schema="schema" v-model="model"></form-generator>
<h2>Changing model</h2>
{{ model }}
</div>