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

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/

Related

v-select component from child to parent binding when editing

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.

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>

Dynamically disable option in Vuetify overflow component

Vuetify disabled prop directly disabled input itself.I am trying to disable individual select option as per content on page.If we pass disabled="string" to items array (static arrangement).
How to make it dynamic.I have made Codepen for the same https://codepen.io/spider007/pen/eYmLBOG
<div id="app">
<v-app id="inspire">
<v-container>
<v-overflow-btn
class="my-2"
:items="items"
label="Overflow Btn - Dense"
dense
v-model="recordToAdd"
></v-overflow-btn>
<p v-if =" recordToAdd === '1' ">A's content is here -- i.e, Option A must be disabled in selection option</p>
</v-container>
</v-app>
</div>
JS
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
recordToAdd : "",
items: [
{text:"A",value:"1"},
{text:"B",value:"2"},
{text:"C",value:"3"},
],
}),
})
You just need to add disabled: true in the data.
<template>
...
<v-overflow-btn
class="my-2"
:items="dropdown"
label="Test"
segmented
target="#dropdown-example"
/>
...
</template>
...
data() {
return {
disabled: false,
dropdown: [
// Always disabled
{ text: 'disabled option', callback: () => console.log('disabled'), disabled: true },
// disable depending on another variable
{ text: 'depending', callback: () => console.log('Hello'), disabled: this.disabled },
{ text: 'other Option', callback: () => console.log('Option') },
]
}
},

Data lost on vue dynamic component when back to parent component

I'm using Vue-form-wizard to create multi-step form. in one of the steps, I'm loading a dynamic component, and pass the data using v-bind and props to dynamic component. the data updates prefectly when we are on the dynamic component, but when I go to the next step all the changes lost on and rests to the parent component defaults! I tried but that doesn't do anything. here is the example of what happens:
the html:
<div id="app">
<div>
<form-wizard #on-complete="onComplete">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component" v-bind={'form':form}></component>
</tab-content>
</form-wizard>
</div>
</div>
and the js will be:
Vue.use(VueFormWizard)
Vue.component('step1', {
template:` <div> My first tab content <br>
</div>`,
props:['form'],
created(){
this.form = 'that other form';
alert(this.form);
}
}
)
Vue.component('step2', {
template:`<div> My second tab content </div>`
})
Vue.component('step3', {
template:`<div> My third tab content </div>`
})
Vue.component('step4', {
template:`<div> Yuhuuu! This seems pretty damn simple </div>`
})
new Vue({
el: '#app',
data() {
return {
form:'this form',
tabs: [{title: 'Personal details', icon: 'ti-user', component: 'step1'},
{title: 'Is Logged In?', icon: 'ti-settings', component: 'step2', hide: false},
{title: 'Additional Info', icon: 'ti-location-pin', component: 'step3'},
{title: 'Last step', icon: 'ti-check', component: 'step4'},
],
}
},
methods: {
onComplete: function(){
alert(this.form);
}
}
})
please help me.
thanks alot
Here:
props:['form'],
created(){
this.form = 'that other form';
alert(this.form);
}
you are changing a prop. Don't do that.
All props form a one-way-down binding between the child property and
the parent one: when the parent property updates, it will flow down to
the child, but not the other way around. This prevents child
components from accidentally mutating the parent’s state, which can
make your app’s data flow harder to understand.
Instead, the child should emit an event that the parent acts on to update the data item. I like to create a computed in the child to wrap up that behavior so I can treat it like a variable that I can assign to.
In the snippet below, I use the .sync modifier to make the update clean. I also call $nextTick after setting the value to give the event time to be handled.
Vue.use(VueFormWizard)
Vue.component('step1', {
template: ` <div> My first tab content <br>
</div>`,
props: ['form'],
computed: {
proxyForm: {
get() {
return this.form;
},
set(v) {
this.$emit('update:form', v);
}
}
},
created() {
this.proxyForm = 'that other form';
this.$nextTick(() =>
alert(this.proxyForm));
}
})
Vue.component('step2', {
template: `<div> My second tab content </div>`
})
Vue.component('step3', {
template: `<div> My third tab content </div>`
})
Vue.component('step4', {
template: `<div> Yuhuuu! This seems pretty damn simple </div>`
})
new Vue({
el: '#app',
data() {
return {
form: 'this form',
tabs: [{
title: 'Personal details',
icon: 'ti-user',
component: 'step1'
},
{
title: 'Is Logged In?',
icon: 'ti-settings',
component: 'step2',
hide: false
},
{
title: 'Additional Info',
icon: 'ti-location-pin',
component: 'step3'
},
{
title: 'Last step',
icon: 'ti-check',
component: 'step4'
},
],
}
},
methods: {
onComplete: function() {
alert(this.form);
}
}
});
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-form-wizard#0.8.4/dist/vue-form-wizard.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vue-form-wizard#0.8.4/dist/vue-form-wizard.min.css" rel="stylesheet" />
<div id="app">
<div>
<form-wizard #on-complete="onComplete">
<tab-content v-for="tab in tabs" v-if="!tab.hide" :key="tab.title" :title="tab.title" :icon="tab.icon">
<component :is="tab.component" :form.sync="form"></component>
</tab-content>
</form-wizard>
</div>
</div>

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>