Using V-model on a loop - javascript

I am trying to use v-model on v-for loop and its throwing an error.
How can i get this to work
<ul class="">
<li class="" v-model="category.data" v-for="category in categories" :key="category.id">
<input :id="'checkbox'+ category.id" type="checkbox" #change="categoriesComputed($event)" :value="category.slug">
<label :for="'checkbox'+ category.id">
{{category.title | capitalize}}
<span>{{category.job_posts | countObj | toNumber}} Jobs</span>
</label>
</li>
</ul>
And in Vue
<script>
export default {
data(){
return {
type: [],
categories: [],
category: {
data: [],
},
}
},
}
</script>

V-model only works if it’s being used on an input element or a custom component that emits a value event that supplies the value you want v-model to be updated with.
https://v2.vuejs.org/v2/guide/forms.html
https://jsfiddle.net/amcquistan/grq3qj36/
V-model is demo in this fiddle

You have to use v-model in the <input> tags.
const app = new Vue({
el: '#app',
data: {
categories: [
{ id: 1, slug: true, title: 'FOO', job_posts: 'Foo'},
{ id: 2, slug: false, title: 'BAR', job_posts: 'Bar'},
{ id: 3, slug: true, title: 'BAZ', job_posts: 'Baz'}
]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<ul class="">
<li class="" v-for="category in categories" :key="category.id">
<input
:id="'checkbox'+ category.id"
type="checkbox"
v-model="category.slug">
<label :for="'checkbox'+ category.id">
{{category.title}} {{category.slug}}
</label>
<input v-model="category.title"/>
</li>
</ul>
</div>

Related

vue.js - Is there any way to render elements in two different divs using one v-for loop?

The goal is to make the output look like this:
<div id="tabs">
<div id="first">
<a>tab 1</a>
<a>tab 2</a>
</div>
<div id="second">
<a>tab 3</a>
</div>
</div>
Currently I'm using this solution (using two v-for loops):
tabs.js (current)
export default {
data() {
return {
tabs: {
first: [{ name: 'tab1' }, { name: 'tab2' }],
second: [{ name: 'tab3' }],
}
}
}
template: `
<div id="tabs">
<div id="first">
<a v-for="tab in tabs.first">{{ tab.name }}</a>
</div>
<div id="second">
<a v-for="tab in tabs.second">{{ tab.name }}</a>
</div>
</div>
`
}
I had an idea to do something like this but it performs more iterations than in the case with two loops:
tabs.js (idea)
export default {
data() {
return {
tabs: {
test: [
{ name: 'tab1', category: 'first' },
{ name: 'tab2', category: 'first' },
{ name: 'tab3', category: 'second' }
]
}
}
}
template: `
<div id="tabs">
<div v-for='category in ["first", "second"]' :id='category' :key='category'>
<template v-for="tab in tabs.test">
<a v-if="tab.category === category">{{ tab.name }}</a>
</template>
</div>
</div>
`
}
I read this topic but it contains slightly different solutions, which unfortunately didn't work in this case.
There's no problem using more than one v-for loops. And there's no problem using nested v-for loops.
The problem I see with your current code is that it's not scalable. You're hard-coding the exact values of your tabs in <template />(e.g: first, second).
The main idea here is to loop through tabs and, inside each tab, to loop through each contents, without the <template> needing to know what the tab is or how many there are.
So that when you change your tabs to, say...
{
tab1: [{ name: 'intro'}],
tab2: [{ name: 'tab2-1' }, { name: 'tab2-2' }],
tab3: [{ name: 'tab3' }]
}
template still works, without needing any change.
To achieve this type of flexibility, you need to use a nested v-for loop:
<div id="tabs">
<div v-for="(items, name) in tabs" :key="name" :id="name">
<a v-for="(item, key) in items" :key="key" v-text="item.name"></a>
</div>
</div>
Demo:
new Vue({
el: '#app',
data: () => ({
tabs: {
tab1: [{
name: 'intro'
}],
tab2: [{
name: 'tab2-1'
}, {
name: 'tab2-2'
}],
tab3: [{
name: 'tab3'
}]
}
})
})
#tabs a { padding: 3px 7px }
<script src="https://v2.vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="tabs">
<div v-for="(links, name) in tabs" :key="name" :id="name">
<a v-for="(link, key) in links"
:key="key"
:href="`#${link.name}`"
v-text="link.name"></a>
</div>
</div>
</div>
But I'd take it one step further and change the tabs to be an array:
data: () => ({
tabs: [
[{ name: 'intro'}],
[{ name: 'tab1' }, { name: 'tab2' }],
[{ name: 'tab3' }]
]
})
And use :id="'tab-' + name" on tab divs if you really need those unique ids. (Hint: you don't).
It makes more sense to me.
I did not see any harm in using two v-for (one for the object keys and another for the array elements) as far as it is all dynamic. You can give a try to this solution by using of Object.keys() :
new Vue({
el: '#app',
data: {
tabs: {
first: [{ name: 'tab1' }, { name: 'tab2' }],
second: [{ name: 'tab3' }],
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="tabs">
<div v-for="tab in Object.keys(tabs)" :key="tab" :id="tab">
<a v-for="tab in tabs[tab]">{{ tab.name }}</a>
</div>
</div>
</div>
You could add a computed property being the .concat from both and loop for it
export default {
data() {
tabs: {
first: [{ name: 'tab1' }, { name: 'tab2' }],
second: [{ name: 'tab3' }],
}
},
computed: {
tabsCombined () {
return this.tabs.first.concat(this.tabs.second)
}
},
template: `
<div id="tabs">
<div v-for='category in tabsCombined' :id='category' :key='category'>
<template v-for="tab in tabs.test">
<a v-if='tab.category === category>{{ tab.name }}</a>
</template>
</div>
</div>
`
}

Why checkbox automatically checked the next checkbox when checked

I'm making a small todolist app using vue. The problem is the next task's checkbox is automatically checked when I click the first checkbox to mark as complete task but it does not happen when I click the last checkbox.
I've tried this on my machine but didn't work. https://jsfiddle.net/razvantirboaca/9aqsjj30/
<div id="app">
<h1>Incomplete Tasks</h1>
<ul>
<li v-for="task in incompleteTasks">
<label>
<input type="checkbox" v-model="task.completed">
{{ task.description }}
</label>
</li>
</ul>
<h1>Complete Tasks</h1>
<ul>
<li v-for="task in completeTasks">
<label>
<input type="checkbox" v-model="task.completed">
{{ task.description }}
</label>
</li>
</ul>
new Vue({
el: '#app',
data: {
tasks: [
{ description: 'First Task', completed: false },
{ description: 'Second Task', completed: false },
{ description: 'Third Task', completed: false },
{ description: 'Fourth Task', completed: false }
]
},
computed: {
incompleteTasks() {
return this.tasks.filter(task => !task.completed);
},
completeTasks() {
return this.tasks.filter(task => task.completed);
}
}
})
Add a key to v-for elements. If there is a unique id you can use that, but since I don't see it in the current data, I have used description as an example
<li v-for="task in incompleteTasks" :key="task.description">
<li v-for="task in completeTasks" :key="task.description">

How to select v-radio by clicking on v-list item vue + vuetify

The problem:
I'm trying to select a radio button by clicking on a list item.
In vuetify manual, this is achieved with a checkbox statically
#9 Action with title and sub-title
My jsfiddle code: https://jsfiddle.net/tgpfhn8m/
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<div id="app">
<template>
<div>
<v-list two-line>
<v-radio-group :mandatory="true" v-model="selectedItem">
<template v-for="(item, index) in items">
<v-list-tile #click="itemType({'name': item.name, 'id':item.id })">
<v-list-tile-action>
<v-radio :value="item.name" :key="item.id"
></v-radio>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{ item.name }}</v-list-tile-title>
<v-list-tile-sub-title>{{ item.description }}</v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-radio-group>
</v-list>
</div>
</template>
</div>
Vue.use(Vuetify);
const items = [{
id: 1,
name: 'James',
description: "If you don't succeed, dust yourself off and try again.",
},
{
id: 2,
name: 'Fatima',
description: 'Better late than never but never late is better.',
},
{
id: 3,
name: 'Xin',
description: 'Beauty in the struggle, ugliness in the success.',
}
]
var vm = new Vue({
el: '#app',
data: {
items,
selectedItem: '',
},
methods: {
itemType(payload) {
//this.$store.commit('item/setIteminStore', payload)
}
}
})
What is the best way to achieve this dynamically with a radio?
Noting that: Clicking around the radio button selects the radio but not on the list item.
Using vue#2.5.9, vuetify# 0.17.1
you can use onclick to set the selected.
itemType(payload) {
this.selectedItem = payload.name
}
and use watch to store the item for either the user click te radio button or list item the store function is called like this :
watch: {
selectedItem (val) {
console.log(val)
this.$store.commit('item/setIteminStore', payload)
}
},
i have make the fiddle https://jsfiddle.net/zacuwyw3/2/
Sorry, I don't know vuetify. But the solution will be similar.
Below is one pure Vue sample to implement what you need. I think it can be transplanted to vuetify easily.
It is simple, just bind :checked of <input type="radio"> with "team === selectedTeam".
new Vue({
el: '#boxes',
data: {
checked: null,
//checkedNames: [],
teams: [{'text':'team1', 'field':'team1'},{'text':'team2', 'field':'team2'},{'text':'team3', 'field':'team3'}],
selectedTeam: null
},
methods: {
selectOneTeam: function (team) {
this.selectedTeam = team
}
}
})
<div id='boxes'>
<div>
<div>
<p>What you selected:</p>
<a v-for="(team, index) in teams" :key="index">
<span>{{team.text}}:</span>
<input type="radio" name="team" :checked="team === selectedTeam"/>
</a>
</div>
<hr>
<ul>
<li v-for="(team, index) in teams" :key="index" style="width: 100px; float:left" #click="selectOneTeam(team)">
{{team.text}}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

Vue how to traverse the list correctly?

I just writed the vue simple code, But unable to follow the HTML effect. After traversal rendering a bit wrong. If gift object is no, for example the goods object has two data, goods_b1 + goods_b2. But i want to follow the HTML effect. Go to the HTML still. And go to the vue loops.
I want to the this effect:
Look at the javascript:
var app = new Vue({
el: "#app",
data: {
list: [{
id: 1,
name: 'A',
goods: [{
name: "goods_a1"
}],
gift: [{
name: "gift_a1",
}]
}, {
id: 2,
name: 'B',
gift: [],
goods: [{
name: "goods_b1"
}, {
name: "goods_b2"
}],
}, {
id: 3,
name: 'C',
goods: [{
name: "goods_c1"
}, {
name: "goods_c2"
}, {
name: "goods_c3"
}],
gift: [{
name: "gift_c1",
}]
}]
}
})
HTML:
<div id="app">
<div class="mui-row" v-for="item in list">
<div class="span-title-main">
<span class="span-title">{{item.name}}</span>
</div>
<br>
<ul>
<li>
<div class="mui-col" v-for="items in item.goods">
<span class="span-name">{{items.name}}</span>
</div>
<div class="addspan">+</div>
<div class="mui-col" v-for="itemss in item.gift">
<span class="span-name">{{itemss.name}}</span>
</div>
<div class="addspan">+</div>
</li>
</ul>
</div>
</div>
Are you asking that the (+) being inside the loop of your goods and gift ?
<div id="app">
<div class="mui-row" v-for="item in list">
<div class="span-title-main">
<span class="span-title">{{item.name}}</span>
</div>
<br>
<ul>
<li>
<div class="mui-col" v-for="items in item.goods">
<span class="span-name">{{items.name}}</span>
<div class="addspan">+</div>
</div>
<div class="mui-col" v-for="itemss in item.gift">
<span class="span-name">{{itemss.name}}</span>
</div>
</li>
</ul>
</div>
</div>
Edit: Remove the (+) for gifts loop as requested by OP.
Note: if the OP is asking to have a style for element in between goods and gift. I would suggest to use the css :last selector with a display:none to have this kind of effect.
It looks like the only difference is that you want a + button to appear after each item.goods instead of just one after the loop.
So put it inside the loop:
<template v-for="items in item.goods"><!-- using "template" to avoid modifying your html structure; you could of course use any tag -->
<div class="mui-col">
<span class="span-name">{{items.name}}</span>
</div>
<div class="addspan">+</div>
</template>
<div class="mui-col" v-for="items in item.gift">
<span class="span-name">{{items.name}}</span>
</div>
<!-- your image doesn't show a + button after gifts, so I've omitted it here -->

Vue.js Passing data to content scope

I'm trying to pass data into inserted (transcluded) template.
In Angular, this would be done by modifying scope passed into transcludeFn, but I can't figure out how to do it in Vue since it simplifies things (I LOVE IT) and takes care of transclusion internally.
I've tried using v-with directive on <content> but it seems like it doesn't work for it.
Simplified component source
<template>
<div>
<ul>
<li v-repeat="tab: tabs">
<content></content>
</li>
</ul>
</div>
</template>
<script>
module.exports = {
data: function () {
return {
tabs: [{ name: 'First' }, { name: 'Second' }]
};
}
};
</script>
Usage
<div v-component="my-component">
<pre>{{ $data | json }}</pre>
</div>
Expected output
<div>
<ul>
<li>
<pre>{ "name": "First" }</pre>
<pre>{ "name": "Second" }</pre>
</li>
</ul>
</div>
Actual output
<div>
<ul>
<li>
<pre>{"tabs": [{ "name": "First" }, { "name": "Second" }]}</pre>
<pre>{"tabs": [{ "name": "First" }, { "name": "Second" }]}</pre>
</li>
</ul>
</div>
This will not work because transcluded content compiles in the parent scope.
Also now you are using v0.11.x version of Vue. Try to use v0.12.
You can pass data to the component this way:
new Vue({
el: "#app",
data: {
tabs: [{ name: 'First' }, { name: 'Second' }]
},
components: {
'my-comp': {
template: "#mycomp",
props: ['tabs'],
}
}
})
<template id="mycomp">
<div>
<ul>
<li v-repeat="tab: tabs">
<pre>{{tab | json }}</pre>
</li>
</ul>
</div>
</template>
<div id="app">
<my-comp tabs="{{tabs}}" />
</div>
<script src="https://rawgit.com/yyx990803/vue/dev/dist/vue.js"></script>

Categories

Resources