How to set pre-selected value in vue js dropdownlist - javascript

I'm working on two vue component.sending parent component array data to child component using props.now i want to set pre-selected value in child component dropdownlist.
Here is my code sample:
props:{
// pre-selected value based on this.
userdata:{
type:[Array,Object],
required:true,
},
roles:{
type:[Array,Object],
required:true,
},
},
data(){
return{
mutableRoles:[],
}
},
and this is my view part:
//i want set pre-selected value on this dropdownlist
<select multiple v-model="mutableRoles" class="form-control">
<option v-for="(role,index) in roles" v-bind:value="role.id" >{{role.name}}</option>
</select>
I have seen many example where show only using string. but in my case both are array.

Try this:
const CurrentRole = Vue.component("current-role", {
template: `
<div>
<label>Options</label>
<select v-model="roleId" #change="changeValue">
<option v-for="v in roles" :key="v.id" :value="v.id">{{v.title}}</option>
</select>
</div>
`,
props: {
userdata: {
type: [Array, Object],
required: true,
},
roles: {
type: [Array, Object],
required: true,
}
},
data: _ => ({
roleId: null
}),
methods: {
changeValue() {
this.userdata.role = this.roles.find(e => e.id == this.roleId)
},
},
mounted() { // for initial state
this.roleId = this.userdata.role.id
},
watch: {
userdata(v) { // for changes on parent
if (v) this.roleId = v.role.id
}
}
})
new Vue({
el: "#app",
data: {
rlist: [{
id: 1,
title: "a"
}, {
id: 2,
title: "b"
}, {
id: 3,
title: "c"
}],
user: {
role: {
id: 3,
title: "c"
}
}
},
methods: {
changeUser() {
this.user = {
role: {
id: 1,
title: "a"
}
}
}
}
})
<script src="https://unpkg.com/vue#2.5.22/dist/vue.js"></script>
<div id="app">
<p>User: {{user}}</p>
<current-role :userdata="user" :roles="rlist">
</current-role/>
<button #click="changeUser">change user</button>
</div>
The select is tailored for primitive values, therefore you'll need to add helper functions.
Higher level vue frameworks such as vue-material, vuetify, element and muse-ui tend to offer components to cope with such problems with a higher abstraction level.
EDIT:
I changed the snippet in order to make it closer to your situation.

Related

Vue2: Binding v-model via computed property?

I have this component that shows a whole bunch of different components. Like so:
computed: {
...mapGetters('forms', ['formErrors']),
input: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
},
},
component() {
const components = {
'ls-text-field': () =>
import('../../../../common/ls-text-field.vue'),
'simple-date-picker': () =>
import('../../../../common/simple-date-picker.vue'),
select: 'v-select',
combobox: 'v-combobox',
};
return components[this.setting.component];
},
attributes() {
const attrs = {
'ls-text-field': {
label: this.setting.name,
},
'simple-date-picker': {},
select: {
label: 'Select this foo',
items: this.setting.options,
},
combobox: {
'return-object': true,
items: this.productList,
loading: this.loading_product_list,
'item-value': 'sku',
'item-text': 'sku',
label: this.setting.name,
name: this.setting.key,
},
};
return {
...attrs[this.setting.component],
'error-messages': this.formErrors(this.setting.key),
};
},
},
and the Template looks something like this:
<template>
<v-col md="4" cols="12">
<component
:is="component"
v-bind="attributes"
v-model="input"
:search-input.sync="searchSku"
/>
But you'll notice I had to do v-model in the template and not in the computed property. I suppose there is NO way to do this:
attributes() {
const attrs = {
'ls-text-field': {
label: this.setting.name,
},
'simple-date-picker': {},
select: {
label: 'Select this foo',
items: this.setting.options,
},
combobox: {
'return-object': true,
items: this.productList,
loading: this.loading_product_list,
'item-value': 'sku',
'item-text': 'sku',
label: this.setting.name,
name: this.setting.key,
'v-model': this.item.info.someKey // This doesn't seem possible
},
};

Vue.js update parent data with an input from a child component

I'm using Vue.js 2 and I'm trying to update the description of a file using an input in a child component. I've been reading a few related questions and read some of the official docs along with .sync but I'm struggling to get the result I want since files is a list of objects.
Here's what I've been trying.
Vue.component('myComponent', {
props: ["file"],
data() {
return {
myDescription: '',
}
},
mounted() {
this.myDescription = this.file.description;
},
template: `
<div>
<label>{{ file.name }}</label>
<br>
<input type="text" #input="update" :value="myDescription"></input>
<br><br>
</div>
`,
methods: {
update() {
this.$emit("update-description", this.myDescription, this.file);
},
}
})
var app = new Vue({
el: '#app',
methods: {
updateDescription(description, file) {
console.log(description);
}
},
data: {
files: [{
id: 1,
name: "Hello",
description: "",
},
{
id: 2,
name: "World",
description: "Foo",
},
{
id: 3,
name: "John",
description: "Bar",
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div> {{ files }} </div>
<br>
<my-component v-for="file in files" :key="file.id" :file="file" #update-description="updateDescription" />
</div>
You're almost there, you can see in the code you've provided that the child component event is being emitted but the value is empty. The problem is you're not updating myDescription, if you change your :value to v-model then it will update, as v-model uses two way binding.
Also, if you want to update the file description, you can just do:
file.description = description;
Vue.component('myComponent', {
props: ["file"],
data() {
return {
myDescription: '',
}
},
mounted() {
this.myDescription = this.file.description;
},
template: `
<div>
<label>{{ file.name }}</label>
<br>
<input type="text" #input="update" v-model="myDescription"></input>
<br><br>
</div>
`,
methods: {
update() {
this.$emit("update-description", this.myDescription, this.file);
},
}
})
var app = new Vue({
el: '#app',
methods: {
updateDescription(description, file) {
console.log(description);
file.description = description;
}
},
data: {
files: [{
id: 1,
name: "Hello",
description: "",
},
{
id: 2,
name: "World",
description: "Foo",
},
{
id: 3,
name: "John",
description: "Bar",
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div> {{ files }} </div>
<br>
<my-component v-for="file in files" :key="file.id" :file="file" #update-description="updateDescription" />
</div>

VueJS : how to set a default value of a key / value in a prop of type array?

I'm learning Vue JS and I am creating my components.
I'm stuck on an issue.
I would like to have my component taking in params an array of object like that :
data() {
return {
items: [
{
text: 'Admin',
href: '#'
},
{
text: 'Manage',
href: '#'
},
{
text: 'Library',
active: true
}
]
}
So, I tried to implement my component with props :
props: {
items: {
type: Array,
required: true
}
However, I have no idea how to say that : if items does not contains active key, then it should have a false default value.
If you have any advices/links or explanations, I will be very grateful to you.
You can make a computed property which will fill-in those active: false by default, and override it with the provided items:
props: {
items: {
type: Array,
required: true
}
},
computed: {
normalizedItems() {
return this.items.map(x => ({ active: false, ...x }));
}
}
You would then use normalizedItems in your template instead of items

Ignore some properties in a deep watch for VueJs

I'm working on an autosave feature for a vue app that sends data to api every time a change is made to the vue app data. Is it possible to ignore certain properties of an objected when using a vue watch? The object has multiple values that I want to watch to auto save and only 1 or 2 that would be ignore so it doesn't seem to make sense to set up a watch function for all the properties I want but instead just ignore the 1 that I don't.
This is the basic structure of the data:
data:{
template: {
name: "Template",
id: 1,
variables: [
{
name: "v1",
color: "#fff",
group: 1,
isSelected: true
},
{
name: "v2",
color: "#fff",
group: 3,
isSelected: false
}
]
}
}
and the basic watch function:
watch: {
template: {
handler: function(){
this.save();
},
deep: true
}
}
The isSelected field for the variables in the template are used for UI purposed only and I would like the watch to ignore that field changing because they don't get saved. I don't want to have to set up a watch function for every field in variables but instead do something in the watch like:
ignore: "template.variables.isSelected"
You can't get old value for mutation object so I think creating some helper data as below temp(save old data) will help your problem .Then check old and new data ....
var app = new Vue({
el: "#app",
data:{
a: 1,
template: {
name: "Template",
id: 1,
variables: [
{
name: "v1",
color: "#fff",
group: 1,
isSelected: true
},
{
name: "v2",
color: "#fff",
group: 3,
isSelected: false
}
]
},
temp: {}
},
mounted: function() {
// this.template.variables[0].isSelected = false;
this.temp = JSON.parse(JSON.stringify(this.template));
this.$set(this.template.variables[0],"isSelected", 222);
},
watch : {
template: {
handler: function(changeVal) {
var flag = true;
for(var i in changeVal.variables) {
if(changeVal.variables[i].isSelected != this.temp.variables[i].isSelected) {
flag = false;
}
}
this.temp = JSON.parse(JSON.stringify(this.template)); // assign changed data as old data again for next process
if(flag) console.log("saveData");// this.save();
else console.log("notsave");
},
deep: true
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Vue removing wrong HTML node in dynamic list of components

I'm experimenting with Vue.JS and composing components together with dynamically.
There's a strange issue where although it seems to be updating the data correctly, if I remove one of the boxes with the call to splice() it always removes the last item in the rendered HTML.
Here's an example fiddle. I'm testing in Chrome.
https://jsfiddle.net/afz6jjn0/
Just for posterity, here's the Vue component code:
Vue.component('content-longtext', {
template: '#content-longtext',
props: {
model: { type: String, required: true },
update: { type: Function, required: true }
},
data() {
return {
inputData: this.model
}
},
methods: {
updateContent(event) {
this.update(event.target.value)
}
},
})
Vue.component('content-image', {
template: '#content-image',
})
Vue.component('content-list', {
template: '#content-list-template',
props: {
remove: { type: Function, required: true },
update: { type: Function, required: true },
views: { type: Array, required: true }
},
methods: {
removeContent(index) {
this.remove(index)
},
updateContent(index) {
return (content) => this.update(index, content)
},
},
})
Vue.component('content-editor', {
template: '#content-editor',
data() {
return {
views: [
{type: 'content-longtext', model: 'test1'},
{type: 'content-longtext', model: 'test2'},
{type: 'content-longtext', model: 'test3'},
{type: 'content-longtext', model: 'test4'},
{type: 'content-longtext', model: 'test5'},
],
}
},
methods: {
newContentBlock(type) {
this.views.push({type: 'content-longtext', model: ''})
},
updateContentBlock(index, model) {
this.views[index].model = model
},
removeContentBlock(index) {
this.views.splice(index, 1)
},
},
})
let app = new Vue({
el: '#app'
})
I've managed to fix the issue thanks to this documentation.
The crux of it is if you don't have a unique key already, you need to store the array index of the object in the object itself, this is because as you mutate the source array you are also mutating it's keys and as far a Vue is concerned when it renders, the last item is missing, not the removed item.
views: [
{index: 0, type: 'content-longtext', model: 'test1'},
{index: 1, type: 'content-longtext', model: 'test2'},
{index: 2, type: 'content-longtext', model: 'test3'},
{index: 3, type: 'content-longtext', model: 'test4'},
{index: 4, type: 'content-longtext', model: 'test5'},
],
...
newContentBlock(type) {
this.views.push({index: this.views.length, type: 'content-longtext', model: ''})
},
Once you have stored the array index you need to add the :key binding to the iterator in the template, and bind that stored value.
<div v-for="(currentView, index) in views" :key="currentView.index">
<component :is="currentView.type" :model="currentView.model" :update="updateContent(index)"></component>
<a v-on:click="removeContent(index)">Remove</a>
</div>
Finally you must make sure you preserve the integrity of your indexes when you mutate the array.
removeContentBlock(index) {
this.views
.splice(index, 1)
.map((view, index) => view.index = index)
},
https://jsfiddle.net/afz6jjn0/5/

Categories