Reputation: 746
I'm having this list of blogposts called articles
that I call from the content folder.
async asyncData ({ $content }) {
const articles = await $content('', { deep: true })
// .where({ cat: 'A' })
.only(['title', 'description', 'img', 'slug', 'cat'])
.sortBy('createdAt', 'asc')
.fetch()
return { articles }
}
This fetches all of my articles and returns a list of them.
Now, I would like to populate my different categories in my website following this structure :
<template>
<div class="container">
<div v-for="article in articles" :key="article">
{{ article.title }}
</div>
<pre> {{ articles }}</pre>
<div v-for="accordion in accordions" :key="accordion.title" class="l">
<Item>
<template #title>
{{ accordion.title }}
</template>
<template #content>
<div> {{ accordion.text }}</div>
</template>
</Item>
</div>
<!-- here goes R -->
<div class="r" />
</div>
</template>
<script>
import Item from '../components/List-item.vue'
export default {
components: { Item },
async asyncData ({ $content }) {
const articles = await $content('', { deep: true })
// .where({ cat: 'A' })
.only(['title', 'description', 'img', 'slug', 'cat'])
.sortBy('createdAt', 'asc')
.fetch()
return { articles }
},
data () {
return {
accordions: [
{
title: 'A',
text: 'Projects from content/A'
},
{
title: 'B',
text: 'Projects from content/B'
},
{
title: 'C',
text: 'Projects from content/C'
}
]
}
</script>
It's using slots from a componenent :
<template>
<div class="wrapper">
<div
:class="{ active: isActive }"
@click="toggle"
>
<a class="title">
<slot name="title" />
</a>
<div v-show="show" :class="{ active: isActive }" class="content">
<slot name="content" />
</div>
</div>
</div>
</template>
I've nested my array into a v-for, but I don't know how to dynamically group that array by URL or by category.
Upvotes: 2
Views: 1475
Reputation: 46610
This is how I would achieve that while keeping it fully dynamic and less error prone.
<template>
<div class="container">
<div v-for="(filteredArticles, categoryKey) in groupedCategories" :key="categoryKey">
<h2>{{ categoryKey }}</h2>
<list-item v-for="article in filteredArticles" :key="article.slug">
<template #title>
{{ article.title }}
</template>
<template #content>
<div> {{ article.description }}</div>
</template>
<br>
</list-item>
<hr>
</div>
</div>
</template>
<script>
export default {
async asyncData ({ $content }) {
const articles = await $content('', { deep: true })
.only(['title', 'description', 'img', 'slug', 'cat', 'dir'])
.sortBy('createdAt', 'asc')
.fetch()
return { articles }
},
computed: {
groupedCategories () {
return this.articles.reduce((finalObject, obj) => {
const directory = obj.dir
finalObject[directory] ?? (finalObject[directory] = [])
finalObject[directory].push(obj)
return finalObject
}, {})
}
}
}
</script>
The .only(['dir'])
gives us all the directories aka /A
, /B
etc, so that it's fully dynamic and not relying on OP's needing to remember to put a cat
in the .md
file.
It is generated for any kind of content as shown here and avoids us some ugly and complex regex subgroups.
Then we're using a reduce
to properly group all the articles depending on their respective directories, filtering on dir
as explained before.
The Logical Nullish Assignment operator in the documentation didn't worked for me for some reason, hence I replaced it with it's alternative aka finalObject[directory] ?? (finalObject[directory] = [])
to basically set a new array in case of a non-existing key.
The idea is to get an object with a key for each directory, itself containing an array of all the articles.
This is how it looks locally + Vue devtools.
A github repo can be found here: https://github.com/kissu/AppPortfolio
Useful since it's a bit cumbersome to mock all kind of .md
files.
A deployed version can be found here: https://papaya-truffle-df823c.netlify.app/
Upvotes: 2
Reputation: 62686
If the intent is to see the list of articles queried in an accordion that divides them by "cat", you can augment the accordion data structure in data to include the articles found in the query...
async asyncData ({ $content }) {
const articles = await $content('', { deep: true })
.only(['title', 'description', 'img', 'slug', 'cat'])
.sortBy('createdAt', 'asc')
.fetch()
// set accordions as an array of grouped articles
const groups = articles.reduce((acc, article) => {
if (!acc[article.cat]) acc[article.cat] = {
title: article.cat,
text: `Articles about ${article.cat}`,
articles: [] // <-- gather the articles for this cat here
};
acc[article.cat].articles.push(article);
return acc;
}, {});
this.accordions = Object.values(groups);
}
The markup should iterate the accordion
in data (which are really categories), and then nest iteration of the articles in each category...
<div v-for="accordion in accordions" :key="accordion.title" class="l">
<Item>
<template #title>
{{ accordion.title }}
</template>
<template #content>
<div> {{ accordion.text }}</div>
<ul>
<li v-for="(article, i) in accordion.articles" :key="i">
{{article.title}}
</li>
</ul>
</template>
</Item>
</div>
Upvotes: 3
Reputation: 376
You can use conditional queries with @nuxt-content
module:
{
async asyncData ({ $content }) {
const articlesC = await $content('', { deep: true })
.where({ cat: { $contains: 'C' } })
// Or .where({ cat: { $eq: 'C' } })
.only(['title', 'description', 'img', 'slug', 'cat'])
.sortBy('createdAt', 'asc')
.fetch()
return { articles }
}
}
And the create an array for each category.
If you do not want to create a query for each category you can use the Array.filter
(or Array.reduce
) method to group your articles big query.
Upvotes: 0