VueJS - Component inside of v-for - javascript

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

Related

Elegant way to use dynamic components with props in Vue

Following case: I have an API outputting all the content for my Page, as well as its structure and so on (for better understanding, imaging an CMS which includes kind of a page builder, where an author can place components by drag and drop to generate pages content, which is delivered to the front-end by that api).
The structure of the api output would look something like:
{content: [
{component: hero, content: {...} },
{component: form, content: {...} },
...
]}
So to generate related content I would think of using dynamic components like:
<template v-for="item in content">
<component :is="item.component" />
</template>
However, doing so I would face the problem, that I have to add properties data onto my components somehow, which (as far as I could see) isn't described within the Vue documentation. So now I wonder how to pass props onto dynamic components, which have entirely different props (hero might have an image, form could have input-placeholders, and so on) - any ideas???
Take a look at v-bind https://v2.vuejs.org/v2/guide/components-props.html#Passing-the-Properties-of-an-Object (same as Vue 3).
Assuming your API includes a props property for each component, then you'd do this:
<component v-for="item in content" :is="item.component" v-bind="item.props"></component>
Nowadays it's necessary to state what version of Vue do you work with.
With Vue 2 you could do it like this:
Vue.component('FirstComponent', {
props: ['title', 'prop1_1'],
template: `
<div>
{{ title }}<br />
{{ prop1_1 }}
</div>
`
})
Vue.component('SecondComponent', {
props: ['title', 'prop2_1', 'prop2_2'],
template: `
<div>
{{ title }}<br />
{{ prop2_1 }}<br />
{{ prop2_1 }}
</div>
`
})
new Vue({
el: "#app",
data() {
return {
items: [
{
component: 'FirstComponent',
props: {
title: 'First component title',
prop1_1: 'this is prop 1_1'
},
},
{
component: 'SecondComponent',
props: {
title: 'Second component title',
prop2_1: 'this is prop 2_1',
prop2_2: 'this is prop 2_2',
},
},
]
}
},
template: `
<div>
<component
v-for="(item, idx) in items"
:key="idx"
:is="item.component"
v-bind="item.props"
></component>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

'v-bind' directives require an attribute value

I am trying to create some type of tree with vue.js and stuck on a problem with element props. Help me out plz.
I've tried :content="{{tempCont}}" and I've tried content="{{tempCont}}", but none of them worked.
Here's the place where I am using tree element:
<div id="tree">
<treeItem :title="Parent" :content="{{tempCont}}"></treeItem>
</div>
Here's the entire tree element:
<template>
<div>
<p v-on:click="openTree">{{title}}</p>
<div id="childs" v-if="childVisibility">
<treeItem v-for="item in content" :key="item" title=item>
</div>
</div>
</template>
<script>
export default {
data: {
childVisibility: false
},
methods: {
openTree: function(){
childVisibility = !childVisibility;
}
},
props: {
title: String,
content: Array,
}
}
</script>
<style scoped>
</style>
I am getting this error:
Use like this: :content="tempCont"
<div id="tree">
<treeItem :title="Parent" :content="tempCont"></treeItem>
</div>
Ok so first of all, when you v-bind something like v-bind:title or :title, what you bind is expressed as a javascript expression.
So if you want your title attribute to be the string Parent, you need either to write it like a native html attribute title="Parent" (notice the lack of :), or as a vue bound attribute v-bind:title="'Parent'" or :title="'Parent'" (notice the use of '' to express a string primitive type in javascript.
Now, the {{ variable }} syntax is used inside vuejs template but you do not need to use it inside v-bind attributes since they are already interpreted as javascript.
So you shouldn't write this:
<div id="tree">
<treeItem :title="Parent" :content="{{tempCont}}"></treeItem>
</div>
but this instead:
<div id="tree">
<treeItem title="Parent" :content="tempCont"></treeItem>
</div>
SincetempCont is already a valid javascript expression.
You don't really need {{}} for passing attributes.
<treeItem :title="Parent" :content="tempCont"></treeItem>
This shall be good enough to work. The puspose of {{}} is to print data and not pass attributes.
Also, in your tree component, it's a good practice to follow object notations in your props. For ex:
props: {
title: {
type: String
},
content: {
type: Array
},
}
Also you should make your components data reactive and making sure that childVisibility is set to this instance rather than a direct reference by setting it like this
export default {
data() {
return {
childVisibility: false
}
},
methods: {
openTree() {
this.childVisibility = !this.childVisibility;
}
},
props: {
title: String,
content: Array,
}
}

Adding vue components based on condition in v-for

I have a JS array formatted similar to
[{type:'text', data: 'some text'},{type: 'image', data: 'link/to/image'}]
for the different values of type I have different vue components (<text-block>, <image-block>) and I want to use a v-for to loop over this array and based on the type, create the right vue component.
The examples for v-for show creating the same element many times like many <li>. Is there a way I can create different elements in a v-for?
You can just use v-if:
<div v-for="(loop, index) in loops" :key="index">
<text-block v-if="loop.type === 'text'"></text-block>
<image-block v-if="loop.type === 'image'"></image-block>
</div>
You can also use dynamic components:
<div v-for="(loop, index) in loops" :key="index">
<component :is="loop.type + '-block'"></component>
</div>
Make sure you have imported the components and defined them on the instance.
You can do something like this, say there's list of movies in an array:
<div id="app">
<ul>
<li v-for="movie in movies">{{ movie }}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
movie: ['some movie 1', 'movie 2', 'idk movies man']
}
});
setTimeout(function() {
app.movies.push('random movie');
}, 2000);
</script>

Vue.js - How to set multiple values (array) for a component over a property?

I have some vue.js components, all these components get their data from outside.
Example:
<vue-button icon="fa-arrow-right" text="mytext"></vue-button>
This works great so far but now I have to set multiple values over the property.
This does not work:
<vue-list items="['Entry1', 'Entry2']"></vue-list>
How can I set multiple values over one property?
Update
I have a working example but I am not sure if thats the right way to go but it works. If someone knows a better way I would be happy if you share it with me/us.
This is how I call the component:
<vue-list times='[ "08:00 - 12:00", "13:00 - 21:00" ]'></vue-list>
And this is the code of the component:
<template>
<div>
<div v-for="item in timesArray">
<span v-html="item"></span>
</div>
</div>
</template>
<script>
export default {
props: ['times'],
data: function() {
return {
timesArray: [],
}
},
created: function() {
this.timesArray = JSON.parse(this.times);
}
}
</script>
Use the attribute binding syntax.
<vue-list :times='[ "08:00 - 12:00", "13:00 - 21:00" ]'></vue-list>
Note the colon in front of :times. You don't need to parse or use data.
It is not because you missed to close the Entry 2 string?
<vue-list items="['Entry1', 'Entry2]"></vue-list>
The right code is
<vue-list items="['Entry1', 'Entry2']"></vue-list>
Think it should work if the component logic is okay.
You have to pass it using an vue instance variable only, see this fiddle.
<div id="app">
<child :items="items"></child>
</div>
where items in defined as vue instance data:
new Vue({
el: '#app',
data: {
full_name: "Initial Val",
items: ['Entry1', 'Entry2']
}
})

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() }}.

Categories