I am creating a list of check boxes via a result set. I can see the correct values but I am not able to set the value correctly
<ul>
<li v-for="role in roles">
<input type="checkbox" :value="role.id" v-model="form.roleIds" > {{role.name}}
</li>
</ul>
When I click on one of the check boxes, I see all of them clicked.
That's what I see in console:
I think your data property roleIds is not defined properly, it should be like this -
roleIds: []
Test.vue
<template>
<ul>
<li v-for="(role, i) in roles" :key="i">
<label>
<input type="checkbox" :value="role.id" v-model="form.roleIds" >
{{role.name}}
</label>
</li>
</ul>
</template>
<script>
export default {
data: () => ({
form: {
roleIds: [] // **this is the catch**
},
roles: [{
id: 1,
name: 'Siddharth'
},
{
id: 2,
name: 'Arora'
}]
})
}
</script>
Console the roles array and confirm there is value for id in every entries of roles. It seems like the value for id is null.
Related
I am working on a Vue Assignment. For styling I am using BootstapVue. What I need to achieve is, whenever an user is entering a text in the input field, a filtered array that is containing the value should be displayed as dropdown. The User can either press Enter key stroke or select the value from dropdown. If the input text is not in the array, then the user should not be able to enter the value i.e. the tag might not be created. For example, whenever we are creating a question here in Stack Overflow, upon typing tags and entering or selecting from the suggestion cards. I I need similar functionality except that if the input text is not in the suggestion array, the user will not be able to create the tag
Here's what I have tried so far :
App.vue
<template>
<div>
<b-form-tags
v-model="value"
#input="resetInputValue()"
tag-variant="success"
:state="state"
>
<template v-slot="{ tags, inputId, placeholder, addTag, removeTag }">
<b-input-group>
<b-form-input
v-model="newTag"
list="my-list-id"
:id="inputId"
:placeholder="placeholder"
:formatter="formatter"
#keypress.enter="addTag(newTag)"
></b-form-input>
</b-input-group>
<b-form-invalid-feedback :state="state">
Duplicate tag value cannot be added again!
</b-form-invalid-feedback>
<ul v-if="tags.length > 0" class="mb-0">
<li
v-for="tag in tags"
:key="tag"
:title="`Tag: ${tag}`"
class="mt-2"
>
<span class="d-flex align-items-center">
<span class="mr-2">{{ tag }}</span>
<b-button
size="sm"
variant="outline-danger"
#click="removeTag(tag)"
>
remove tag
</b-button>
</span>
</li>
</ul>
<b-form-text v-else>
There are no tags specified. Add a new tag above.
</b-form-text>
</template>
</b-form-tags>
<datalist id="my-list-id">
<option>Manual Option</option>
<option v-for="(size, ind) in sizes" :key="ind">{{ size }}</option>
</datalist>
</div>
</template>
<script>
export default {
data() {
return {
newTag: "",
value: [],
sizes: ["Small", "Medium", "Large", "Extra Large"],
};
},
computed: {
state() {
// Return false (invalid) if new tag is a duplicate
return this.value.indexOf(this.newTag.trim()) > -1 ? false : null;
},
},
methods: {
resetInputValue() {
this.newTag = "";
},
formatter(value) {
if (this.sizes.includes(value)) {
return value.toUpperCase();
}
return ;
},
},
};
</script>
How can I achieve the same? Thanks
You can achieve it by using b-form-datalist with the use of <b-form-input> by adding a change event to reset the input value if there is no data in the dataList as per the search value.
Demo :
new Vue({
el: '#app',
data: {
sizes: ['Small', 'Medium', 'Large', 'Extra Large'],
tag: null
},
methods: {
checkTag(e) {
const tagsAvailable = this.sizes.filter((item) => item === e);
if (!tagsAvailable.length) {
this.tag = ''
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.21.2/bootstrap-vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.21.2/bootstrap-vue.min.css"/>
<div id="app">
<b-form-input list="my-list-id" v-model="tag" v-on:change="checkTag($event)"></b-form-input>
<datalist id="my-list-id">
<option>Manual Option</option>
<option v-for="size in sizes">{{ size }}</option>
</datalist>
</div>
I generate so many input boxes on loop of article array.
article is array of objects. Input boxes values are set with "value" key in each object.
<div
v-for="(option, i) in this.article"
:key="i"
class="form-row"
>
<div class="col">
<input
v-model="option.value"
:label="'label '+i"
:name="'inputValue'+i"
type="text"
required
/>
</div>
</div>
<button #click="submit"></button>
<script>
export default {
name: 'Article',
data(){
return(
article:[
{id: 'art1', value: 'artValue1'},
{id: 'art2', value: 'artValue2'},
{id: 'art3', value: 'artValue3'}
// There are about 50 objects
]
)
},
methods:{
submit(){
// How get values of inputs?
}
}
}
</script>
How can I make the value of input change and update the object in vue?
I created a sample Single File Component based on your code. Look at my modifications. The 'magic' of Vue reactivity is that the 'article' data is updated in real time as you change the input values.
<template>
<div class="input-bound-object-array">
<div class="row">
<div class="col-md-6">
<form #submit.prevent="submitForm">
<div class="form-group" v-for="(option, i) in article" :key="i">
<label>{{ option.id }}</label>
<input type="text" class="form-control" v-model="option.value"
required />
</div>
<button type="submit" class="btn btn-secondary">Submit</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
article: [
{ id: 'art1', value: 'artValue1' },
{ id: 'art2', value: 'artValue2' },
{ id: 'art3', value: 'artValue3' }
// There are about 50 objects
]
}
},
methods: {
submitForm() {
// 'this.article' is updated as the input values are changed, so at this point
// you have the data changes, so you can process it as needed, i.e. POST to REST endpoint
console.log(this.article);
}
}
}
</script>
You forgot to return the object from data function and you can pass option to submit handler and print the input values.
// How get values of inputs? -> Do you mean all of the inputs or input where ever the button clicked or anything else. Below code is to handle individual input and submit handlers
<template>
<div
v-for="(option, i) in article"
:key="i"
class="form-row"
>
<div class="col">
<input
v-model="option.value"
:label="'label ' + i"
:name="'inputValue' + i"
type="text"
required
/>
</div>
<!-- <button #click="submit(option)">Submit</button> to handle individual inputs -->
</div>
<!-- <button #click="submitAll()">Submit All</button> to handle all inputs -->
</template>
<script>
export default {
name: 'Article',
data() {
return { // you missed this
article:[
{id: 'art1', value: 'artValue1'},
{id: 'art2', value: 'artValue2'},
{id: 'art3', value: 'artValue3'}
]
}
},
methods: {
submit(option) { // current option
console.log(option.id, option.value)
},
submitAll() {
console.log(this.article) // which will have update input values as
you're using `v-model`, check `values`
}
}
}
</script>
Demo Link here
I am using Vue-Multiselect plugin and want to know how I can auto populate additional input fields (customer_firstname and customer_email) when a user selects an item from the initial drop down?
Scenario: If the name exists in the database/drop-down list the user can then select that name and Vue will auto populate the rest of the input fields (taken from the options array). Else, if the name does not exist, the user can tag/create a new value for each additional input fields (e.g, customer_firstname, customer_email).
Here's my JSFIDDLE code with evertying working, just need help wiring up the ability to autopopulate the rest of the input fields based on the initial selection of the lastname dropdown.
Vue Template:
<div id="app">
<div>
Last Name:
<multiselect id="dontforget" v-model="customer_last_name" :options="options" placeholder="Select an existing customer or type to add a new one" label="lastname" :custom-label="customerSelectName" track-by="uid" :close-on-select="true" :clear-on-select="false" :hide-selected="true" :preserve-search="true" #select="onSelect" :taggable="true" #tag="addTag" :multiple="false" tag-placeholder="Add customer as new customer">
<template slot="singleLabel" slot-scope="props">{{ props.option.lastname }}</template>
<template slot="option" slot-scope="props">
<span style="font-weight: bold;">{{ props.option.lastname }}</span>, {{ props.option.firstname }} -- <small>{{props.option.email}}</small>
</template>
<template slot="noResult">no rsults brah</template>
</multiselect>
<pre class="language-json"><code>Data: {{ customer_last_name }}</code></pre>
<br>
<br>
<br>
<label for="customer_first_name_input">First Name:</label><br>
<input id="customer_first_name_input" type="text" v-model="customer_first_name" />
<br>
<label for="customer_emailt">Email:</label><br>
<input id="customer_email" type="email" v-model="customer_email" />
</div>
</div>
Vue Script:
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
customer_last_name: '',
customer_first_name: '',
customer_email: '',
options: [{"uid":"1","firstname":"John","lastname":"Doe","email":"johndoe#aol.com","phone":null,"c_organization":"ACME","c_title":null}, {"uid":"2","firstname":"Mary","lastname":"Smith","email":"msmith#aol.com","phone":null,"c_organization":"NBA","c_title":"Miss"}, {"uid":"3","firstname":"Mike","lastname":"Verlander","email":"asdafsdf#aol.com","phone":null,"c_organization":"MLB","c_title":"Mr"}]
},
methods: {
addTag (newTag) {
const parts = newTag.split(', ');
const tag = {
uid: this.options.length + 1,
firstname: parts.pop(),
lastname: parts.pop()
}
this.options.push(tag)
this.customer_last_name = tag;
},
customerSelectName (option) {
return `${option.lastname}, ${option.firstname}`
},
onSelect (option, uid) {
console.log(option, uid)
}
}
}).$mount('#app')
You pretty much have everything you need. Your method onSelect is appended to the select event of your multiselect, so you can just use it.
In your onSelect method, add these lines:
this.customer_first_name = option.firstname;
this.customer_email = option.email;
That will assign to your variables the properties of the object you get when you select an option.
I need a simple v-for to render object properties in a list.
<div
v-if="profileData && profileData.length > 0"
>
<ul
v-for="value in profileData"
:key="value.id"
>
<li>{{value.id}}</li>
</ul>
</div>
In the script:
profileData: {},
created() {
const userId = this.$route.params.userId
const currentUser = this.$store.getters.currentUser
const profileData = this.$store.getters.profileData
console.log('profileData in seeProfile: ', profileData) }
(profileData comes from an api response)
I did exactly the same in two other pages (just rendering different objects) and it worked.
With this code, in the console I get value is undefined.
If I remove :key="value.id" (it becomes red in the editor but it still works), and instead of the list items I type only {{ value }}}, then the object properties get rendered ( but in the ugly format of a js object). How can it be? What am I doing wrong?
Thank you
Your v-if will never show even if profileData has data, because you can't directly check for the length of an Object in javascript.
A few things:
You can't check for the length of an Object, it will return undefined. If you must use an object, then you'd have to check for Object.keys(obj).length.
let obj = {
first: {
name: "first",
meta: "data"
},
second: {
name: "second",
meta: "data"
}
};
console.log("Object.length is: ", obj.length);
console.log("Object.keys().length is: ", Object.keys(obj).length);
You're being redundant, you don't need to check for profileData and its length (and you don't need to > 0), you could simply check for v-if="Object.keys(profileData).length". If the Object has zero entries, then it won't show because if(0) is false.
I'd strongly recommend to work with arrays to iterate with v-for. I'd use computed properties and return an array, and iterate through that. Object reactivity works non-intuitively in JS, so you'll be scratching your head later when you try to find out why stuff isn't updating on your view:
computed: {
listData() {
let list = Object.values(this.profileData);
return list;
}
}
In view:
<div v-if="listData.length"/>
Also, don't use the array's entry index as your :key, because if you have another array with v-for, you'll have duplicated keys in your model. I'd use something like v-for="(item, key) in list" :key="'list-item-' + key"
Put the v-for on the li, not the ul.
<ul>
<li v-for="value in profileData"
:key="value.id">{{value.id}}</li>
</ul>
Also , if your your profileData is an object and not an array, you need to decide if you want to loop through the keys or values.
<ul>
<li v-for="value in Object.values(profileData)"
:key="value.id">{{value.id}}</li>
</ul>
<ul>
<li v-for="value in Object.keys(profileData)"
:key="value.id">{{value.id}}</li>
</ul>
Or use Vue's default behavior.
<ul>
<li v-for="(value,key) in profileData"
:key="value.id">{{value.id}}</li>
</ul>
The api has been changed, so the working code is slightly different from the original one.
Here's the template:
<div
v-if="listData.length"
>
<ul>
<li>Name: <b>{{ profileData.user.first_name }}</b></li>
<li>Surname: <b>{{ profileData.user.last_name }}</b></li>
<li>Username: <b>{{ profileData.user.username }}</b></li>
<li>Car: <b>{{ profileData.if_cars_owned }}</b></li>
<li v-if="profileData.if_cars_owned === true">
Car model: {{ profileData.user_car_type }}
</li>
<li v-if="profileData.if_cars_owned === true">
Car power: {{ profileData.user_car_powered_by }}
</li>
<li>Motorcycle: <b>{{ profileData.if_motorcycle_owned }}</b></li>
<li v-if="profileData.if_motorcycle_owned === true">
Motorcycle model: {{ profileData.user_motorcycle_characteristic }}
</li>
</ul>
</div>
Script:
created(){
const profileData = this.$store.getters.profileData
this.profileData = profileData
console.log('profile data in profilo: ', profileData)
},
I've also updated
<div
v-if="listData.length"
>
and in the script
computed: {
...,
listData() {
let list = Object.values(this.profileData);
return list;
}
},
following the advice of #Adrián S. Basave.
Thanks to anyone who tried to help.
x
сеI have an array with categories. Each category has children.
When a parent is checked/unchecked, its children must be checked/unchecked as well. If all children are checked, the parent must be checked as well.
Vue updates the fields as expected, but doesn't re-render. I cannot understand why.
Here is my code:
<template>
<div class="card">
<div class="card-body">
<ul class="list-tree">
<li v-for="category in categories" :key="category.id" v-show="category.show">
<div class="custom-control custom-checkbox">
<input :id="'category-' + category.id"
v-model="category.checked"
#change="checkParent(category)"
type="checkbox"
class="custom-control-input" />
<label class="custom-control-label"
:for="'category-' + category.id">
{{ category.name }}
</label>
</div>
<ul>
<li v-for="child in category.children" :key="child.id" v-show="child.show">
<div class="custom-control custom-checkbox">
<input :id="'category-' + child.id"
v-model="child.checked"
#change="checkChild(child, category)"
type="checkbox"
class="custom-control-input" />
<label class="custom-control-label" :for="'category-' + child.id">
{{ child.name }}
<small class="counter">({{ child.products_count }})</small>
</label>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
</template>
export default {
data () {
return {
categories: [],
listItemTemplate: { show: true, markedText: null, checked: false }
}
},
methods: {
checkParent (category) {
category.children.forEach(child => {
child.checked = category.checked
})
},
initializeCategories () {
this.categories = []
this.originalCategories.forEach(originalCategory => {
var parent = this.copyObject(originalCategory)
this.categories.push(parent)
parent.children.forEach (child => {
child = this.copyObject(child)
})
})
},
copyObject (category) {
return Object.assign(category, {...this.listItemTemplate})
}
},
computed: {
...mapState({
originalCategories: state => state.categories,
})
},
mounted () {
this.initializeCategories()
}
}
You need to expand your scope, since you are changing it only within checkParent() method, variables that you are making changes to will not have an effect onto components variables.
Use the index instead of value in categories iteration to find correct category, and then apply changes in scope of whole component:
<li v-for="(category, categoryIndex) in categories" :key="category.id" v-show="category.show">
<div class="custom-control custom-checkbox">
<input :id="'category-' + category.id"
v-model="category.checked"
#change="checkParent(categoryIndex)"
type="checkbox"
class="custom-control-input" />
<label class="custom-control-label"
:for="'category-' + category.id">
{{ category.name }}
</label>
</div> <!-- the rest of the code ... -->
And then in component's method:
methods: {
checkParent (categoryIndex) {
let categoryChecked = this.categories[categoryIndex];
this.categories[categoryIndex].children.forEach((child, childIndex) => {
this.categories[categoryIndex].children[childIndex].checked = categoryChecked;
})
},
I fixed it. The problem wasn't related to the checkboxes at all. The problem was related to the way I've created the categories array.
When I initialize the component, I copy the array from vuex and add new properties (like checked) in order to check the children when the parent is checked. I didn't follow the rules for adding new fields, that's why the children wasn't reactive and didn't get checked when the parent was checked.
Thanks a lot for your effort to help me!