I need to add classes to an element, based on categories. Here is my example data:
data() {
return {
projects: [
{
title: 'Project 1',
categories: [{ name: 'featured' }, { name: 'category-1' }],
},
{
title: 'Project 2',
categories: [{ name: 'category-2' }],
},
],
};
},
What I need is to add the categories directly as classes on the wrapper div (with the v-for), that will render:
<div class="featured category-1">
<h3>Project 1</h3>
</div>
<div class="category-2">
<h3>Project 1</h3>
</div>
I'm not sure how to do this?
<div v-for="(project, index) in projects" :class="v-for="(category, index) in project.categories???">
{{project.title}}
</div>
Should I do this differently? I can't seem to figure it out. Thanks for your help!
It's simple:
<div v-for="project in projects" :class="classExtraction(project)">
<h3>
{{project.title}}
</h3>
</div>
You need a method that extracts classes from your project's categories:
methods: {
classExtraction(item) {
return item.categories.map(cat => cat.name);
}
}
http://jsfiddle.net/eywraw8t/371192/
Also, please note that you should use :key directive with v-for binding it to a unique property, preferably object's id:
https://v2.vuejs.org/v2/style-guide/#Keyed-v-for-essential
Related
I'm currently working on a project in Vue that uses the WP-Rest api. I'm bored of tediously nested objects when looping through objects. While I can destructure top-level objects, I'm not sure how make things like this: page.page.featuredImage.node.altText less ugly. Can anyone offer any good practices for handling nested objects within Vue loops?
<div v-for="(page, index) in pageGrid" :key="index"
class="flex flex-col overflow-hidden rounded-lg shadow-lg">
<div v-if="page.page.featuredImage" class="flex-shrink-0">
<img class="h-48 w-full object-cover" :alt="page.page.featuredImage.node.altText"
:sizes="page.page.featuredImage.node.sizes" :srcset="page.page.featuredImage.node.srcSet" />
Sometimes that is why to do it in the other hand you can map over your data when mounted for instant and return a new array that looks the way you want.
I am not sure how your data looks but I think you could also do something like this:
<div v-for="(page, index) in pageGrid.page" :key="index">
As per your template code, You are having below data structure contains by pageGrid.
[
{
page: {
featuredImage: {
node: {
altText: 'Text 1',
sizes: 'small',
srcSet: 'image src link'
}
}
}
},
{
page: {
featuredImage: {
node: {
altText: 'Text 1',
sizes: 'small',
srcSet: 'image src link'
}
}
}
},
...
...
]
To make this simple, You can move the whole node object into a separate array with the help of Array.map() method and then it will easy to iterate in the template.
pageGrid.map(({ page }) => page.featuredImage.node);
Live Demo :
new Vue({
el: '#app',
data: {
pageGrid: [
{
page: {
featuredImage: {
node: {
altText: 'Text 1',
sizes: 'small',
srcSet: 'image 1 src link'
}
}
}
},
{
page: {
featuredImage: {
node: {
altText: 'Text 2',
sizes: 'medium',
srcSet: 'image 2 src link'
}
}
}
}
],
updatedPageGrid: []
},
mounted() {
this.updatedPageGrid = this.pageGrid.map(({ page }) => page.featuredImage.node);
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(page, index) in updatedPageGrid" :key="index">
{{ page.altText }}
{{ page.sizes }}
{{ page.srcSet }}
</div>
</div>
Very simple question. I'm learning VueJS and have created a simple component:
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
I then have parsed some data to it like this:
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
My question is how can get the title of a specefic element based on the id in my HTML? For now I can only render through the items and get them all, but I want to be able to specify which title I want to display based on the Id. Here is my HTML which gives me all the data:
<div id="blog-post-demo">
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"
></blog-post>
</div>
You can achieve with COMPUTED property, like that
<template>
<div id="blog-post-demo">
<p v-for="post in thisOne" :key="post.id" >
{{post.title}}
</p>
</div>
</template>
<script>
export default {
el: '#blog-post-demo',
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
},computed: {
thisOne(){
return this.posts.filter(x => x.id === 3); /*choose your id*/
}
}
};
</script>
Or you can use event too to select the id of the posts to display (more dynamically)
Tip: If you start with VueJS, learn about the properties of VueJs (DATA, COMPUTED, CREATED, METHOD) and look at the uses and strengths of each one. For my part, the VueJS site is very very well done for beginners: https://v2.vuejs.org/v2/guide/
I'm not sure if I understand correctly what you want to do. But if you want to go through all posts and display title of particular post then you can try this way:
<blog-post
v-for="post in posts"
:key="post.id"
:title="setTitle(post)"
/>
(: instead of :v-bind it's a short form, also if you don't pass slots in your component you can go with self-closing tag)
Then in your methods section you can create a method:
setTitle(post) {
if(post.id === 2) return post.title
}
I have a problem with add meta information from graphql to vue-meta.
how to from external about.gql information add to vue-meta?
Adding one more gql query I would like to avoid. So I am not so experienced with Vue and Apollo and any help will be good!
Thanks a lot!
about.vue
<ApolloQuery :query="require('../graphql/about.gql')">
<template slot-scope="{ result: { loading, data } }">
<div v-if="loading">Loading...</div>
<div class="" v-if="data">
<div class="row">
<div class="col-xs-12">
<div class="p40" v-html="data.pageBy.content"></div>
</div>
</div>
</div>
</template>
</ApolloQuery>
<script>
export default {
data: () => ({
loading: 0,
error: null,
}),
metaInfo: {
// title will be injected into parent titleTemplate
title: 'About',
meta: [
{
name: 'description',
content:
'Epiloge is about connecting in your field of interest. Our vision is to help people share their knowledge, work, projects, papers and ideas and build their network through what they do rather where they live, study or work.'
},
{
property: 'og:title',
content: 'Epiloge - Build your network in your field of interest'
},
{ property: 'og:site_name', content: 'Epiloge' },
{ property: 'og:type', content: 'website' },
{ name: 'robots', content: 'index,follow' }
]
}
}
</script>
In my project, I use vue.js.
I want to display content of list with nested loop。 In parent page, i have defined:
<template>
<div>
<detail-header></detail-header>
......
<detail-list></detail-list>
</div>
</template>
The component of detail-list is :
<template>
<div>
<div v-for="(item, index) of list" :key="index">
<div class="item-title border-bottom">
<span class="item-title-icon"></span>
{{item.title}}
</div>
<div v-if="item.children" class="item-children">
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DetailList',
props: {
list: Array
},
data () {
return {
list: [{
title: 'adult',
children: [{title: 'threePeople',children: [{ title: 'threePeople-w'}]}, {title: 'fivePeople'}]
}, {
title: 'student'
}, {
title: 'child'
}, {
title: 'offer'
}]
}
}
}
</script>
unlucky, I got a error message:
Duplicated key 'list' of list: [{ in detail-list
who can help me ?
If you want this to work, keep the list in props (and remove it from DetailList's data) and define in your parent page's data.
So the first DetailList and its children will have the list as a prop.
So you'll have in the parent page :
<template>
<div>
<detail-header></detail-header>
......
<detail-list :list="list"></detail-list>
</div>
</template>
<script>
export default {
name: 'Parent',
data () {
return {
list: [{ ... the list ... }]
}
}
I wonder if I use array as model for multiple-checkbox list, how can I check which items is checked and which are unchecked efficiently rather than compare one by one with that array?
<ul>
<li v-for="task in tasks">
<input type="checkbox" :id="task.title" :value="task" v-model="selectedTasks" #change="handleTasks(task)">
<label :for="task.title">{{task.title}}</label>
</li>
</ul>
new Vue({
data: {
tasks: [
{ title: 'Go to the store' },
{ title: 'Clean the house' },
{ title: 'Learn Vue.js' }
],
selectedTasks: []
},
})
You could add a property to each task (e.g., checked) and bind that to each input's v-model, making it trivial in code to check whether a task is checked/selected:
new Vue({
el: '#app',
data() {
return {
tasks: [
{ title: 'Go to the store', checked: false },
{ title: 'Clean the house', checked: false },
{ title: 'Learn Vue.js', checked: false }
]
};
},
methods: {
clearCheckedTasks() {
this.tasks = this.tasks.filter(x => !x.checked);
}
}
})
<script src="https://unpkg.com/vue#2.5.16"></script>
<div id="app">
<ul>
<li v-for="task in tasks">
<input type="checkbox" :id="task.title" v-model="task.checked">
<label :for="task.title">{{task.title}}</label>
</li>
</ul>
<button #click="clearCheckedTasks">Clear checked tasks</button>
<h3>tasks (live)</h3>
<pre>{{tasks}}</pre>
</div>
based on your comment that "I also want to know if one item is checked or unchecked when I click on it", I would use the DOM Event object to detect if it's checked.
demo: https://jsfiddle.net/jacobgoh101/m480bupd/6/
add #click="clickHandler" to the input
<input type="checkbox" :id="task.title" :value="task" v-model="selectedTasks" #change="handleTasks(task)" #click="clickHandler">
use e.target.checked to get the checked
methods: {
clickHandler(e) {
console.log(e.target.checked);
},
// ...
}
Use the loop index:
<li v-for="(task, index) in tasks">
<input type="checkbox" :id="task.title" :value="task" v-model="selectedTasks[index]" #change="handleTasks(task)">
<label :for="task.title">{{task.title}}</label>
</li>