Vue "item is not defined" in a "v-for" - javascript

I'm trying to use v-for to render for each item, but I got this:
vue.js:616 [Vue warn]: Error in render: "ReferenceError: item is not defined"
found in
---> <Welcome>
<Main>
<Root>
I tried to comment some codes like this:
<div style="padding-top: 20px" v-for="(item,index) in weekRank" v-bind:key="index">
<b>{{item.username}} </b> {{item.point}} / 10
<div v-if="item.point>10" class="progress deep-purple lighten-3" style="flex-grow: 1;height: 16px;">
<!--<div class="determinate deep-purple darken-1" :style="getProgressBarStyle(item.point)"></div>-->
</div>
<div v-else class="progress blue lighten-3" style="flex-grow: 1;height: 16px;">
<!--<div class="determinate blue darken-1" :style="getProgressBarStyle(item.point)"></div>-->
</div>
</div>
But the errors are still there. It seems the problem is not caused by getProgressBarStyle but by <div v-if="item.point>10" or codes above it, because they point where item was referred.
So I commented these:
<!--<<div v-else class="progress blue lighten-3" style="flex-grow: 1;height: 16px;">
<div class="determinate blue darken-1" :style="getProgressBarStyle(item.point)"></div>
</div>-->
And now the errors disappear, but why? I commented these html codes which are supposed to be not related.
I've reproducted this problem with all required code here (Press F12 to see the errors, please)
Preview:
<div style="padding-top: 20px" v-for="(item,index) in weekRank" v-bind:key="index">
<b>{{item.username}} </b> {{item.point}} / 10
<div v-if="item.point>10" class="progress deep-purple lighten-3" style="flex-grow: 1;height: 16px;">
<div class="determinate deep-purple darken-1" :style="getProgressBarStyle(item.point)"></div>
</div>
<div v-else class="progress blue lighten-3" style="flex-grow: 1;height: 16px;">
<div class="determinate blue darken-1" :style="getProgressBarStyle(item.point)"></div>
</div>
</div>

The problem is you are attempting to reference item inside method getProgressBarStyle(), but you name the parameter to this method as todo. You just need to update todo to item. Also I'd consider returning an object for the :style assignment instead of a string. Also you need to probably pass item instead of index to this method in the template as you are attempting to use properties on item such as point:
HTML:
<div class="determinate blue darken-1" :style="getProgressBarStyle(item)"></div>
JS
new Vue({
el: "#app",
data: {
weekRank: [
{ index: 0, username: "Learn JavaScript", point: 9 },
{ index: 1, username: "Learn Vue", point: 7 },
{ index: 2, username: "Play around in JSFiddle", point: 5 },
{ index: 3, username: "Build something awesome", point: 1 }
]
},
methods: {
getProgressBarStyle: function(item) { // change this to 'item'
if (item.point >= 10) return { 'width': 100%' };
return { 'width': item.point * 10 + '%' };
}
}
})
Here is a working example.
Hopefully that helps!

Looking at your Vue component code, you seem to have some errors on your getProgressBarStyle method where you're giving a todo parameter but are referencing item which throws that error. I've used the template code you've provided in the snippet below as well
new Vue({
el: "#app",
data: {
weekRank: [
{ index: 0, username: "Learn JavaScript", point: 9 },
{ index: 1, username: "Learn Vue", point: 7 },
{ index: 2, username: "Play around in JSFiddle", point: 5 },
{ index: 3, username: "Build something awesome", point: 1 }
]
},
methods: {
getProgressBarStyle: function(point){
if (point >= 10) return 'width: 100%';
return 'width: ' + point * 10 + '%'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<h3>Rank</h3>
<body>
<div class="" style="display: flex;">
<div style="min-width: 300px;flex-grow: 1;">
<h6><b>details: </b></h6>
<div style="padding-top: 20px" v-for="(item,index) in weekRank" v-bind:key="index">
<b>{{item.username}} </b> {{item.point}} / 10
<div v-if="item.point>10" class="progress deep-purple lighten-3" style="flex-grow: 1;height: 16px;">
<div class="determinate deep-purple darken-1" :style="getProgressBarStyle(item.point)"></div>
</div>
<div class="determinate blue darken-1" :style="getProgressBarStyle(item.point)"></div>
</div>
</div>
</div>
</div>
</body>
</div>
</div>

Related

How can I separate values from reusable checkbox in vuejs?

My question might not match to the problem that I am facing, So, I will explain it in detail here.
This is code for SeparateTechnology.vue. I have removed a few methods to make code shorter.
<template>
<div>
<div v-for="mss in ms" :key="mss.name">
<section class="container shadow-box techno-box" v-if="mss.name==selected">
<p>Review the software criteria below and ensure that you complete each section</p>
<h4>{{mss.name}}</h4>
<div class="row shadow-box">
<div class="col-sm-12 col-lg-6">
<p>Select all that apply</p>
<div class="dropdown">
<input
v-model.trim="inputValue"
class="dropdown-input"
type="text"
placeholder="Search for your Solution"
/>
<div class="dropdown-list" v-show="selected">
<div
v-show="itemVisible(item)"
v-for="item in mss.subMenu"
:key="item"
class="dropdown-item"
>
{{ item }}
<button
class="button button-mini button-dark button-rounded itemLabel"
#click="selectSolution(item,mss)"
>
<i class="icon-line-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-sm-12 col-lg-6 rightList">
<div class="dropdown-list" v-show="selected" v-if="mss.selected_solutions!=''">
<div v-for="item in mss.selected_solutions" :key="item" class="dropdown-item">
{{ item }}
<button
class="button button-mini button-dark button-rounded deleteLabel"
#click="deleteSelectedItem(item,mss)"
>
<i class="icon-line-minus"></i>
</button>
</div>
</div>
<div v-else>
<div class="style-msg errormsg">
<div class="sb-msg">
<i class="icon-remove"></i>You have not selected any solutions.
</div>
</div>
</div>
<button
class="button button-mini"
#click="clearUserOptions(mss)"
v-if="mss.selected_solutions.length > 1"
>
<i class="icon-line-cross"></i>Clear all selection
</button>
</div>
<div style="padding:20px;"></div>
</div>
<div class="row">
<div class="col-sm-12 col-md-3 inputTitle">
<h5>Don't see it in the list above:</h5>
</div>
<input
class="col-sm-12 col-md-6"
type="text"
v-model="value"
#keydown.enter="getUserSolution(value,mss)"
placeholder="Enter your solution here.. "
/>
</div>
<div style="padding:20px;"></div>
<div class="row shadow-box">
<h5
class="col-sm-12"
>Identify how the software solution is leveraged within your organization</h5>
<div
v-for="item in mss.selected_solutions"
:key="item"
class="clearfix col-sm-12 col-md-6"
style="padding:20px"
>
<span v-if="mss.isDifferent=='campaign'">
<div class="card">
<h5 class="card-header">{{item}}</h5>
<CheckBox
:groups="campaignMangment"
name="campaignMangment"
:value="item"
classProp="col-sm-12"
#clicked-show-detail="clickedShowDetailModal"
/>
{{item}} and {{productSelected}}
</div>
</span>
</div>
</div>
<button class="btn btn-primary" #click="postUserDetails(mss)">Submit</button>
</section>
</div>
</div>
</template>
<script>
import CheckBox from "../../../components/checkbox/Checkbox";
export default {
name: "technology",
data() {
return {
usageValue: "",
value: "",
campainCheckedNames: [],
checked: "",
productSelected: [],
checkedValues: "",
exists: null,
inputValue: "",
campaignMangment: [
"Business to Customer (B2C)",
"Business to Business (B2B)",
"Customer to Customer (C2C)",
"Customer to Business (C2B)",
"E-Government",
"M-Commerce",
],
};
},
props: ["ms", "selected"],
//method to show all the solutions that contains the user
methods: {
clickedShowDetailModal: function (campainCheckedNames) {
console.log(campainCheckedNames);
this.productSelected.push(campainCheckedNames);
},
},
components: {
CheckBox,
},
};
</script>
This is CheckBox.vue
<template>
<div class="row col-mb-0">
<div
:class="classProp + ' checkbox-margin'"
v-for="singleSelector in groups"
:key="singleSelector"
>
<div>
<input
:id="singleSelector+groupId"
:value="singleSelector"
class="checkbox-style"
type="checkbox"
v-model="checkedValues"
#change="showDetailModal"
/>
<label :for="singleSelector +groupId" class="checkbox-style-3-label">{{singleSelector}}</label>
</div>
</div>
</div>
</template>
<script>
let groupId = 0;
export default {
props: {
groups: Array,
name: String,
classProp: String,
value: String,
},
data() {
return {
groupId: groupId++,
checkedValues: [],
inputs: {},
};
},
methods: {
showDetailModal: function (e) {
this.$set(this.inputs, this.value, e.target.value);
this.$emit("clicked-show-detail", this.inputs);
},
},
};
</script>
<style scoped>
.checkbox-margin {
margin-bottom: 10px;
}
</style>
Right Now, the line {{item}} and {{productSelected}} prints output like below as shown in screenshot
.
Problem: On every click/unclick of checkbox it adds an item to an array and not in the format I want. and if I select the checkbox on left only, it adds that item on right as well as shown in the screenshot above. It is due to the same array declaration, but I couldn't think more than that.
Expected Output: For every selected item, I want to print the list of selected checkboxes in an array format like below.
"selected_solutions": [{
"name": "Adobe Campaign",
"usage": [ "Business to Customer",...]
}, {
"name": "Marin Software",
"usage": ["M-Commerce",...]
}]
Even small hints or tips would be more than appreciated. I don't mind posting code on gist if required. Thank you.
With checkbox-components you want to emit "event.target.checked" likeso:
this.$emit('input', event.target.checked)
That way it'll behave like a checkbox and you can use a v-model on the parent:
<CheckBox v-model="item.checked" ...>
I do not use v-model inside the component and instead just bind :checked="value", but that might be up to preference. But using both :value and v-model could (should?) cause problems because v-model is actually a :value + emitter under the hood (or so I heard).
Anyway, here is my minimal reusable checkbox-wrapper:
<template>
<input type="checkbox" :checked="value" #change="handleChange" />
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'MyCheckbox',
props: {
value: { type: Boolean, default: false }
},
methods: {
handleChange(event) {
this.$emit('input', event.target.checked)
}
}
})
</script>
After that is done you can just filter on checked items:
computed: {
checkedSolutions(){
return this.ms[0].selected_solutions
.filter( solution => solution.checked );
}
}

How to filter posts in Vue with components and v-bind:class

I'm trying to make an example based on this pen I got from here:
https://codepen.io/conradolandia/pen/YzyPmrv
But I want to use vue-router, I've tried this: (pen: https://codepen.io/conradolandia/pen/vYNERPW)
HTML:
<main class="wrap">
<div id="app">
<router-view></router-view>
</div>
</main>
<template id="post-list-template">
<div class="container">
<div class="row">
<h4>Filter by album:</h4>
<div class="filters">
<button class="btn" v-bind:class="{ active: currentFilter === 'ALL' }" v-on:click="setFilter('ALL')">all</button>
<button class="btn" v-bind:class="{ active: currentFilter === 'art' }" v-on:click="setFilter('art')">art</button>
<button class="btn" v-bind:class="{ active: currentFilter === 'doodles' }" v-on:click="setFilter('doodles')">doodles</button>
<button class="btn" v-bind:class="{ active: currentFilter === 'workshops' }" v-on:click="setFilter('workshops')">workshops</button>
</div>
</div>
<div class="columns is-multiline">
<div class="column is-3" v-if="currentFilter === post.category || currentFilter === 'ALL'" v-bind:key="post.title" v-for="post in posts">
<div class="card post">
<img class="card-img-top" v-bind:src="post.image">
<div class="card-body">
<div class="card-title">{{ post.title }}</div>
<small class="tags">{{ post.category }}</small>
</div>
</div> <!-- .post -->
</div> <!-- .col-md-4 -->
</div> <!-- .row -->
</div> <!-- .container -->
</template>
CSS:
body{
background-color: #ccc;
box-sizing:border-box;
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
}
.post {
margin-bottom: 20px;
}
.post img{ width: 100%;}
.tags {background-color: #ccc; padding: 3px 5px;}
.filters {
margin-bottom: 20px;
}
JS:
var postList = Vue.extend({
template: "#post-list-template",
data: function(){
return {
currentFilter:'ALL',
posts: [
{title: "Artwork", image: "https://picsum.photos/g/200?image=122", category: 'art'},
{title: "Charcoal", image: "https://picsum.photos/g/200?image=116", category: 'art'},
{title: "Sketching", image: "https://picsum.photos/g/200?image=121", category: 'doodles'},
{title: "Acrillic", image: "https://picsum.photos/g/200?image=133", category: 'workshops'},
{title: "Pencil", image: "https://picsum.photos/g/200?image=134", category: 'doodles'},
{title: "Pen", image: "https://picsum.photos/g/200?image=115", category: 'art'},
{title: "Inking", image: "https://picsum.photos/g/200", category: 'workshops'},
{title: "Artwork", image: "https://picsum.photos/g/200?image=121", category: 'art'},
{title: "Charcoal", image: "https://picsum.photos/g/200?image=115", category: 'art'},
{title: "Sketching", image: "https://picsum.photos/g/200?image=124", category: 'doodles'},
{title: "Acrillic", image: "https://picsum.photos/g/200?image=13", category: 'workshops'},
{title: "Pencil", image: "https://picsum.photos/g/200?image=14", category: 'doodles'},
]
}
},
methods: {
setFilter: function(filter) {
this.currentFilter = filter;
}
},
})
// Start a new instance of router (instead of router.map)
var router = new VueRouter({
routes: [
{ path: '/', component: postList }
]
})
// Start a new instance of the Application required (instead of router.start)
new Vue({
el: '#app',
router: router,
})
So far, no luck. The filter kind of works, the first click I make activates a filtering option, but then all filters stop working, and firefox complains with "TypeError: "e is undefined"".
Can somebody point me in the right direction, please? I don't understand why the first codepen link works but the second doesn't.
Clarifying: When I click any filter, filters kind of work, but if I click the "ALL" filter, everything stops working.
Try using a computed function
computed:{
filteredPosts:function(){
if(this.currentFilter==='ALL'){
return this.posts;
}
return this.posts.filter(post=>{
return post.category === this.currentFilter;
})
}
}
You can use filteredPosts instead of posts while looping
<div class="columns is-multiline">
<div class="column is-3" :key="post.title" v-for="post in filteredPosts">
<div class="card post">
<img class="card-img-top" :src="post.image">
<div class="card-body">
<div class="card-title">{{ post.title }}</div>
<small class="tags">{{ post.category }}</small>
</div>
</div> <!-- .post -->
</div> <!-- .col-md-4 -->
</div> <!-- .row -->
You don't need to use any condition while looping, since the computed function will do the job.

VUE template problem to center text with text-center

I have problem to center h1 tag category.title with bootstrap property "text-center" in vue.js component, the title appears in left side. Everything else, for example category.description is centering correctly. It looks like only title refuses to be influenced by any align.
<template>
<div class="row" v-for="category in laravelData">
<h1 class="text-center">{{ category.title }}</h1>
<p class="text-center pb-3">{{ category.description }}</p>
<div class="col-sm-12 col-md-6 col-lg-6" v-for="project in category.project">
<div class="projects-thumb">
<img :src="/img/ + photo.filename" class="img-responsive" alt="" v-for="photo in project.photo">
<h3 class="pt-3">{{ project.title }}</h3>
<p class="mt-2 p-3">{{ project.description }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
category: {},
laravelData: {},
id: '',
succmsg: true,
showmodal: false,
actionmsg: '',
}
},
methods: {
projectList() {
var id = _.last( window.location.pathname.split( '/' ) );
axios.get('/api/category/'+ id).then((response) => {
this.laravelData = response.data;
});
}
},
mounted() {
this.projectList();
}
}
</script>
Maybe the problem is because of several loops in this template?

vue prop display columns with different sizes wrapped in container

I'm currently working on a page for our company's new website. In this design we have a blog page which displays our blog entries. I request these blogs with Cockpit CMS & axios in my NuxtJs project that makes it a object.
At this moment I'm struggling with the following: In the design the blogs are displayed in rows of 3 items, and after that 2 items. I'm using the BULMA framework and to display my columns the correct way I need to wrap the columns like follow:
<div class="columns">
<div class="column"></div>
<div class="column"></div>
<div class="column"></div>
</div>
<div class="columns">
<div class="column"></div>
<div class="column"></div>
</div>
<div class="columns">
<div class="column"></div>
<div class="column"></div>
<div class="column"></div>
</div>
A short version of my code can be found here:
https://jsfiddle.net/06o5nvkd/
At this moment I have a component which gets my blogs through a prop called 'blogs'. Me and my colleague can't find a good working method to split the blogs array into chunks of 2 & 3 to display them correctly in a wrapping columns div.
Is anyone here able to help us out? Any more information that's needed is welcome.
Use a computed property to chunk your blogs array on the fly and use that array with the chunks to create the columns.
Computed properties detect addtions to the blogs array automatically (when using this.blogs inside), so fetching some new blog entries will be no problem.
new Vue({
el: "#app",
data: {
blogs: [
{ title: "Learn JavaScript" },
{ title: "Learn Vue" },
{ title: "Play around in JSFiddle" },
{ title: "Build something awesome" },
{ title: "a" },
{ title: "b" },
{ title: "c" },
{ title: "d" },
{ title: "e" },
{ title: "f" },
]
},
computed: {
blogsChunked() {
let blogs = this.blogs;
let chunkSize = 2;
let blogsChunked = [];
while (blogs.length > 0) {
let chunk = blogs.slice(0, chunkSize);
blogs = blogs.slice(chunkSize);
blogsChunked.push(chunk);
chunkSize == 2 ? chunkSize++ : chunkSize--;
}
return blogsChunked;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
div.column {
color: white;
background-color: grey;
margin: 5px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<section class="blogs has-padding-bottom-xxl">
<div class="container">
<div class="columns is-mobile" v-for="(chunk, index) in blogsChunked" :key="index">
<div class="column" v-for="(blog, key) in chunk" :key="key">
{{ blog.title }}
</div>
</div>
</div>
</section>
</div>
One solution is to have nested arrays & v-for loops.
<div id="app">
<section class="blogs has-padding-bottom-xxl">
<div class="container">
<div class="columns" v-for="(blogs, key) in blogsArr" :key="key">
<div class="column" v-for="(blog, index) in blogs" :key="index">
{{ blog.title }}
</div>
</div>
</div>
</section>
</div>
new Vue({
el: "#app",
data: {
blogsArr: [
[{ title: "Learn JavaScript" },
{ title: "Learn Vue" }],
[{ title: "Play around in JSFiddle" },
{ title: "Build something awesome" }]
]
}
})
https://jsfiddle.net/3vqydakw/

Why does v-show and v-if respond immediately to boolean changes in objects, but not in arrays?

Came across this weird behaviour when developing a dynamic FAQ-page where the answer is displayed after the question is clicked on.
When the boolean that controls the opening/closing of the answer is stored in an object the view is updated instantly and the answer is displayed or hidden instantly. However, when it is stored in a list, the answer isn't displayed or hidden until the view is updated in some other way.
Here's a fiddle displaying the problem https://jsfiddle.net/masterofginseng/ffqt9n4y/6/
The HTML
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="faq">
<div>This responds immediately</div>
<hr>
<div v-for="questionObj in questions1">
<div style="cursor: pointer;" #click="questionObj.open = !questionObj.open">
{{ questionObj.question }}
</div>
<div style="color: blue;" v-show="questionObj.open">{{ questionObj.answer }}</div>
</div>
<br>
<br>
<div>This doesn't respond until the view is updated in some other way (ex. by clicking on one of the questions above)</div>
<hr>
<div v-for="questionarray in questions2">
<div style="cursor: pointer;" #click="questionarray[2] = !questionarray[2]">
{{ questionarray[0] }}
</div>
<div style="color: blue;" v-show="questionarray[2]">{{ questionarray[1] }}</div>
</div>
</div>
And the javascript:
new Vue({
el: "#faq",
data: {
questions1: [{
question: "How big is it?",
answer: "very big",
open: false
}, {
question: "How small is it?",
answer: "very small",
open: false
}],
questions2: [
["How big is it?", "very big", false],
["How small is it?", "very small", false]
]
}
});
Due to limitations in Javascript, Vue can't detect changes of values in items with this syntax:
questionarray[2] = !questionarray[2]
See here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection
As explained in the link above, you have to use splice() instead:
questionarray.splice(2, 1, !questionarray[2])
This is because of list-rendering caveats
Try like this:
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="faq">
<div>This responds immediately</div>
<hr>
<div v-for="questionObj in questions1">
<div style="cursor: pointer;" #click="questionObj.open = !questionObj.open">
{{ questionObj.question }}
</div>
<div style="color: blue;" v-show="questionObj.open">{{ questionObj.answer }}</div>
</div>
<br>
<br>
<div>This doesn't respond until the view is updated in some other way (ex. by clicking on one of the questions above)</div>
<hr>
<div v-for="questionarray in questions2">
<div style="cursor: pointer;" #click="myMethod(questionarray)">
{{ questionarray[0] }}
</div>
<div style="color: blue;" v-show="questionarray[2]">{{ questionarray[1] }}</div>
</div>
</div>
Script
new Vue({
el: "#faq",
data: {
questions1: [{
question: "How big is it?",
answer: "very big",
open: false
}, {
question: "How small is it?",
answer: "very small",
open: false
}],
questions2: [
["How big is it?", "very big", false],
["How small is it?", "very small", false]
]
},
methods:{
myMethod(questionArray){
this.$set(questionArray, 2, !questionArray[2]);
}
}
});
Here is the jsFiddle
Or a less verbose version as Linus borg answered:
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="faq">
<div>This responds immediately</div>
<hr>
<div v-for="questionObj in questions1">
<div style="cursor: pointer;" #click="questionObj.open = !questionObj.open">
{{ questionObj.question }}
</div>
<div style="color: blue;" v-show="questionObj.open">{{ questionObj.answer }}</div>
</div>
<br>
<br>
<div>This doesn't respond until the view is updated in some other way (ex. by clicking on one of the questions above)</div>
<hr>
<div v-for="questionarray in questions2">
<div style="cursor: pointer;" #click="questionarray.splice(2, 1, !questionarray[2])">
{{ questionarray[0] }}
</div>
<div style="color: blue;" v-show="questionarray[2]">{{ questionarray[1] }}</div>
</div>
</div>

Categories

Resources