Accessing v-for dynamically created elements outside of loop - javascript

I've got a v-for loop and I would like to access the dynamically created item outside of loop. Is it possible?
<li v-for="item in cart.items">
<h1>{{ item.product.name }}</h1>
</li>
<p>{{ access item.product.name outside }}</p>
data () {
return {
cart: {
items: []
},
products: [
{
name: "name"
},
{
name: "name2"
}
]
}
}

I would like to access all the items so I can display the quantity of
them all.
That is a data operation, not a DOM operation. You don't get your data from the DOM. It is in your viewmodel. As you figured out (and noted in your comment), you can make a computed.

You need to get access from the parent. You can put the v-for on a template tag, and do your iteration there.
<template v-for="item in cart.items">
<li >
<h1>{{ item.product.name }}</h1>
</li>
<p>{{ item.product.name }}</p>
</template>
This allows you access to each item. See: Templates and v-for

Related

Vue: why this simple v-for doesn't work: Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key error

I have this simple component that has a person with values that it should display:
TestFor.vue:
<template>
<div v-for = "(value, key) in this.person">
{{key}} : {{value}}
</div>
</template>
<script scoped>
export default {
props: {
person: {
firstName: 'Bob',
lastNmae: 'Andersen',
age: 27
}
}
}
</script>
However I get the Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key error. Which shouldn't occur, because according to documentation: https://v2.vuejs.org/v2/guide/list.html my syntax is valid:
This video tutorial also uses this syntax and it works fine for them. Why doesn't it work for me?
EDIT:
As people have said the require-v-for-key rule is a culprit. Is there any reason behind that rule or is it OK to just disable it?
I've added "vue/require-v-for-key": "off" to package.json and the original code seems to be working now. Am I good or should I enable that rule back? It just seems like an annoyance for no reason.
You need to define key per element of your list. in your case, it's quite trivial:
<div v-for = "(value, key) in this.person" :key='key'>
{{key}} : {{value}}
</div>
Without the key, Vue is unable to link data to components created with v-for. Lack of key means that any modification of the object you iterated would destroy and recreate all the child components, which would bring a number of undesired side effects. Once the key is defined, when collection changes, vue will calculate key for each element, compare it with keys of existing children and act accordingly.
The error is coming from your linter rule: require-v-for-key
You can fix it, with adding the key binding:
<div v-for = "(value, key) in this.person" :key="key">
{{key}} : {{value}}
</div>
The error means you need to bind the key property at your div.
<template>
<div v-for = "(value, key) in person" :key="key">
{{key}} : {{value}}
</div>
</template>
<script scoped>
export default {
props: {
person : {
type : Object,
default : function(){
return {
firstName: 'Bob',
lastNmae: 'Andersen',
age: 27
}
}
}
}
}
}
</script>

v-for returns error when i want to write arrays

I want my array data to be shown in the list, but I'm seeing the following error:
error: Elements in iteration expect to have 'v-bind:key' directives (vue/require-v-for-key) at src\components\Kubismyk.vue:5:9:
My component:
<template>
<div>
<h1>{{ title }}</h1>
<ul>
<li v-for="ninja in ninjas">{{ ninja }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
title:'hello world',
ninjas: ['yoshi','mario','ryu']
}
}
}
</script>
You need to bind a v-key when using v-for:
<ul v-for="ninja in ninjas" v-bind:key="ninja.id">
<li>{{ ninja.name }}
</ul>```
You need a unique key for v-key when using v-for loops so that Vue can track each node's identity. You could use index in this case:
<li v-for="(ninja, index) in ninjas" :key="index">
You can discover more information about this linter rule in vue/require-v-for-key docs.

Filter with VueJS

I am very new with VueJS so my question is very simple. I cannot use vue filter. Please help me fix the problem.
My html file is shown as followed. When I try this code the item in v-for can't be shown and also the it has error Failed to resolve filter: uppercase.
Can any one tell me why?
<div id="pan" class="pan">
<div v-for="item in list|orderBy 'level'" >{{item.id}}</div>
<span>{{message | uppercase}}</span>
</div>
<script type="text/javascript" src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
var pan = new Vue({
el: '#pan',
data: {
list: [
{ name: '東京', id:"TOKYO",level:"2"},
{ name: '全国',id:"JAPAN",level:"1" },
{ name: '関東',id:"KANTO",level:"0" },
],
message:"hello"
}
});
</script>
If you are using vuejs2, with vuejs2 uppercase filter has been removed. You will have to use toUpperCase() for this, like following:
<span>{{message.toUpperCase()}}</span>
see demo.
Similarly orderBy filter also has been removed, vuejs2 suggests to use lodash’s orderBy (or possibly sortBy) in a computed property:
HTML
<p v-for="item in orderedList">{{ item.name }}</p>
vue
computed: {
orderedList: function () {
return _.orderBy(this.list, 'level')
}
}
Here is demo with orderBy.
You can use a computed property.
Markup:
<div id="pan" class="pan">
<div v-for="item in orderedList" >{{ item.id }}</div>
<span class="pan__title">{{ message }}</span>
</div>
Definition inside of Vue:
data(){
sortKey : 'level'
},
computed : {
orderedList(){ return this.list.sort(this.sorter) }
},
methods : {
sorter(a,b){ return a[this.sortKey] > b[this.sortKey] }
}
And then you can change order of the elements in orderedList by modifying sortKey (using v-model="sortKey" to any kind of input, like <select></select> or any other way).
Here is an example based on your code
And what about uppercase, I prefer to control a view with css, and text-transform property can solve this: .pan__title { text-transform: uppercase; }. But you can define a computed property for this one too or keep it inline with {{ message.toUpperCase() }}.

Laravel 5.3, Vue.js 2+ can't get List Rendering (v-for) working

public/js/symp.js (initial post)
new Vue({
el :'#symp',
data :{
tease: 'test',
items: [
{ message: 'Foo', subject: 'A' },
{ message: 'Bar', subject: 'B' }
]
}
});
resources/views/dash.blade.php (initial post)
<html>
<body>
#extends('layouts.app') [ this includes symp.js and calls #yeild('content') ]
#section('content')
<div id="symp">
<div>
<span>subject</span>
<span>message</span>
</div>
tease: <span>#{{ tease }}</span>
<div v-for="item in items">
sub: <span>#{{ item.subject }}</span>
msg: <span>#{{ item.message }}</span>
</div>
</div>
#endsection
I know Laravel fights with {{ }} and so need to prepend #, but my understanding is demo snippet code above should present tease: test then two rows
sub: A msg: Foo
sub: B msg: Bar
... alas, I see the tease: subject message, no tease value, and no rows present at all.
What's hilarious, I have a folder as part of a project I'm building that has working version of http://www.hc-kr.com/2016/11/laravel-5-vuejs-crud-with-notification-pagination-laravel.html in place, however that folder forces Vue.js 1.x, so it seems this is unique issue with Laravel 5.3's vue.js 2.x?
[ updated per #PanJunjie潘俊杰 suggestion :-) ]
resources/views/dash.blade.php
#extends('layouts.app')
#section('content')
<div id="symp">
</div>
#endsection
resources/views/layout/app.blade.php
<html>
<script src="/js/app.js"></script>
<body>
...
<template id="symp-temp">
tease: <span>#{{ tease }}</span>
<div v-for="item in items">
sub: <span>#{{ item.subject }}</span>
msg: <span>#{{ item.message }}</span>
</div>
</template>
</body>
<script src="/js/symp.js"></script>
</html>
public/js/symp.js
new Vue({
el :'#symp',
name: 'symp', /* unnecessary? */
template: '#symp-temp', /* this helps a bit :-) */
data :{
tease: 'well',
items: [
{ message: 'Foo', subject: 'A' },
{ message: 'Bar', subject: 'B' }
]
}
});
Some progress. The tease value now shows, but the item rows still don't show. JavaScript console reports this ...
[Vue warn]: Component template should contain exactly one root element:
tease: <span>{{ tease }}</span>
<div v-for="item in items">
sub: <span>{{ item.subject }}</span>
msg: <span>{{ item.message }}</span>
</div>
This worked fine in Vue 1.x but in vue 2 you need wrap your template around a div tag on :
resources/views/layout/app.blade.php
<template id="symp-temp">
<div>
tease: <span>#{{ tease }}</span>
<div v-for="item in items">
sub: <span>#{{ item.subject }}</span>
msg: <span>#{{ item.message }}</span>
</div>
</div>
</template>
You can checkout the issue on github https://github.com/vuejs/vue-loader/issues/384
The mount point denoted by el will have what's inside it overwritten by vue's rendered html, you shouldn't put your template inside it. Ways to correct it (first one may be best as you may want to keep your templates in your blade files):
Move your template out of <div id="symp"> and tell vue to use it as template like: <template id="symp-temp">...<template> and add a template: '#symp-temp' option to your new Vue. (custom markups are valid in html5 standard)
Move templates to .js files and wrap them in ES6's `s which allows line breaks in quotes. Put the backquoted string in template option.
Use .vue single file components.
Egad, this was an obtuse wild goose chase.
1) Be careful to put extra div's around elements to isolate them. Notice tease now has dedicated div surrounding it, and the item... v-for has a different dedicated div surrounding it. Without that Vue 2+ gets confused and won't play ball.
<template id="symp-temp">
<div>
<div>tease: <span>#{{ tease }}</span></div>
<div v-for="item in items">
sub: <span>#{{ item.subject }}</span>
msg: <span>#{{ item.message }}</span>
</div>
</div>
</template>
2) For some reason this.$set(...) I guess is deprecated (or needs extra scope that I'm not aware of). Using Vue.set with full parameters seems to work though in Vue 2+ world.
Vue.set(this, 'items', response.data.data.data);
I may find more gremlins, but it looks like I'm unstuck for now :-)
For more context, here's more of the code I was trying to enable. I'm doing quite a bit of custom layout experimentation so avoiding components until more of our data schema settles down.
new Vue({
el :'#symp',
name: 'symp',
template: '#symp-temp',
data :{
tease: 'well',
items: [
{ message: 'Foo', subject: 'A' },
{ message: 'Bar2', subject: 'B' }
],
pagination: {
total: 0,
per_page: 2,
from: 1,
to: 0,
current_page: 1
}
},
mounted: function() {
this.getVueItems(this.pagination.current_page);
},
methods: {
getVueItems: function(page) {
/* URL /make is a Laravel resource that pulls rows from a database,
/* returns them in json list, which the Vue.set transfers to the template :-) */
this.$http.get('/make?page='+page).then((response) => {
Vue.set(this, 'items', response.data.data.data);
});
}
}
});

VueJS - Component inside of v-for

I am trying to render a list of objects from my Vue-Instance. Each object should use a component, so I put the component into the v-for-loop. But all I get is list.title and list.text instead of the correct values.
Is there a special way to use components in v-for-loops?
I found this thread in the Vue-Forum, but don't know how to use it or if it's the right way.
App:
<div id="app">
<div v-for="list in lists">
<listcard title="list.title" text="list.text"></listcard>
</div>
</div>
Template:
<template id="listcard-template">
<div class="card">
<h2>{{ title }}</h2>
<p>{{ text }}</p>
</div>
</template>
My component:
Vue.component('listcard', {
template: '#listcard-template',
props: ['title', 'text']
})
Vue-Instance:
new Vue({
el: "#app",
data: {
lists: [
{title: "title1", text: "text1"},
{title: "title2", text: "text2"},
...
]
}
})
Thanks!
You should pass then as dynamic prop using : in front of parameters:
<listcard :title=list.title :text=list.text></listcard>
From documentation:
A common mistake beginners tend to make is attempting to pass down a number using the literal syntax:
<!-- this passes down a plain string "1" -->
<comp some-prop="1"></comp>
However, since this is a literal prop, its value is passed down as a plain string "1", instead of an actual number. If we want to pass down an actual JavaScript number, we need to use the dynamic syntax to make its value be evaluated as a JavaScript expression:
<!-- this passes down an actual number -->
<comp :some-prop="1"></comp>
https://vuejs.org/guide/components.html#Literal-vs-Dynamic

Categories