Home > OS >  Fetch all nested content files and group them by directories in Nuxt content
Fetch all nested content files and group them by directories in Nuxt content

Time:08-22

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.

enter image description here

Now, I would like to populate my different categories in my website following this structure :

<template>
  <div >
    <div v-for="article in articles" :key="article">
      {{ article.title }}
    </div>
    <pre> {{ articles }}</pre>

    <div v-for="accordion in accordions" :key="accordion.title" >
      <Item>
        <template #title>
          {{ accordion.title }}
        </template>

        <template #content>
          <div> {{ accordion.text }}</div>
        </template>
      </Item>
    </div>
    <!-- here goes R -->
    <div  />
  </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 >
    <div
      :
      @click="toggle"
    >
      <a >
        <slot name="title" />
      </a>

      <div v-show="show" : >
        <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.

CodePudding user response:

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" >
  <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>

CodePudding user response:

This is how I would achieve that while keeping it fully dynamic and less error prone.

<template>
  <div >
    <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 enter image description here

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/

CodePudding user response:

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.

  • Related