Retrieve all nested content files and organize them by their respective directories in Nuxt content

My blogposts are stored in a list called articles fetched 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 code retrieves all my articles and generates a list of them.

Now, I want to categorize my website using 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>
    <!-- R goes here -->
    <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>

A component with slots is being used :

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

The array is nested in a v-for loop, but I'm unsure how to dynamically group it by URL or category.

Answer №1

To create a dynamic accordion view that organizes queried articles by category, update the data structure to include the relevant articles...

async asyncData ({ $content }) {
  const articles = await $content('', { deep: true })
    .only(['title', 'description', 'img', 'slug', 'cat'])
    .sortBy('createdAt', 'asc')
    .fetch()

  // group articles by category for accordion display
  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 HTML should loop through the accordion object in data (representing categories), and then iterate over the articles within 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>

Answer №2

Here is my approach to achieving this goal, focusing on maintaining dynamic functionality and reducing the chances of errors.

<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']) method retrieves all directories such as /A, /B, etc., ensuring full dynamism without relying on manually adding a cat parameter in the .md file.
This system works for any content type, eliminating the need for complicated regex subgroups, as illustrated here.

We then utilize a reduce operation to categorize articles based on their respective directories, using the dir attribute described earlier.

The documentation's Logical Nullish Assignment operator didn't function properly, so I replaced it with an equivalent code

finalObject[directory] ?? (finalObject[directory] = [])
to handle non-existent keys by creating a new array.

The main objective here is to create an object with keys representing directories, each containing an array of associated articles.

Below are screenshots of how it appears locally and in Vue devtools.

https://i.sstatic.net/aUGv2.jpg

You can access the GitHub repository at: https://github.com/kissu/AppPortfolio. This resource proves useful for simulating various types of .md files effortlessly.
For a live demonstration, visit:

Answer №3

To implement conditional queries using the @nuxt-content module, you can utilize the following code:

{
  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 }
  }
}

Additionally, you can create an array for each category and use the Array.filter (or Array.reduce) method to group your articles into a single query.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Combine elements from two separate arrays

Is there an efficient method for combining objects from two arrays into a new list in JavaScript? a = [{a:1, b:2, c:3}, {d:1, e:4, f:2}] b = [{m:1, n:2, o:4}, {r:1,s:3,u:5}, {k:1,j:4,f:8}] z = [{a:1, b:2, c:3, m:1, n:2, o:4}, {d:1, e:4, f:2, r:1,s:3,u:5}, ...

Tips for establishing communication between a React Native webView and a React web application

I am currently working on establishing communication between a webView in react-native and a web-app created with React-360 (and React). I am utilizing the react-native-webview library and following a guide for creating this communication. You can find the ...

Update a separate React component in response to the interaction with a different React component

Currently, I am working with a react component named Interest Category that showcases an initial set of Interest categories. Another react component called CreateInterestCategoryDialog, which functions as a modal, is responsible for creating a new entity I ...

Utilizing jQuery for displaying or hiding list elements

To view all the code, click on this link: http://jsfiddle.net/yrgK8/ A section titled "news" is included in the code snippet below: <ul id="news"> <li><p>asfdsadfdsafdsafdsafdsafdsafdsafdsa</p></li> <li>&l ...

Methods for automatically refreshing a vue.js application when there are changes in the SQL table data

I am seeking a way to display various notes in my Vue.js application, such as informing users about upcoming maintenance. I was thinking of utilizing an info "banner" located beneath the application header for this purpose. The information regarding the ...

Accessing a file url with Firefox using protractor

I am currently facing a challenge with Protractor as I try to access an HTML file as a website using it. Whenever I attempt to do so, I encounter an error from Protractor stating: Failed: Access to 'file:///C:/filelocation/index.html' from s ...

Utilizing axios and Vue to access the Twitch API

Currently, my goal is to integrate the Twitch API for user sign-ins, but I've encountered some challenges along the way. Below is the initial code snippet that I have developed: <template> <section> <form class="pt-6 pb-8 animat ...

How to jest at node_modules that provide a function?

I've been working on a typeScript program that interacts with an external API. While writing tests for this program, I've encountered challenges in properly mocking the dependency on the external API to analyze the values passed to the API itself ...

What is the best way to manage numerous query parameters in JavaScript?

I'm currently working on a react app that involves the use of a table component. This particular table has various filtering options, and my goal is to update the URL with these filters so that when I return to the page, the table can adjust based on ...

Using JavaScript to obtain the coordinates of a click event from a programmatically triggered click on an HTML element

Is there a way to programmatically simulate a click event on an HTML DOM element and still retrieve the screenX/screenY and clientX/clientY coordinates successfully? When manually clicking the element, the coordinates are visible in the console, but when t ...

Display the dialog box whenever a user hovers over an element in a Vuet

I am having an issue with displaying an edit button and opening a dialog box on click using mouseenter and mouseleave events. When I hover over the edit button, it is displayed, but as soon as I click on it, it disappears. Can anyone help me understand w ...

Tips for circumventing CORS in an ajax request using various browser extensions or add-ons

I am encountering a cross-origin problem with my WCF service. Despite adding an extension in chrome to bypass CORS, the issue persists. Is there any other extension/add-on/plugin available to test the service locally and bypass the CORS problem? Edit Aft ...

The issue with window.addEventListener failing to update state when the scrollY is not at zero persists during route changes

When using addEventListener in the componentDidMount lifecycle method to add a class to a header based on the scroll behavior, an issue arises when changing routes via a modal or browser back button. The scrolling state is not accurately reflected as the s ...

What methods can be used to prevent the appearance of the ActiveX warning in IE7 and IE8 when dealing with drop

I have a dilemma with my website where I am using JavaScript to modify CSS on hover for specific items. Interestingly, in Firefox everything runs smoothly without any ActiveX message popping up. However, in IE8, I encounter a warning stating "To help prote ...

The Bootstrap 5 navigation bar does not extend to the full width of its parent

While working on a Bootstrap navigation inside a container, I encountered an issue where the navigation bar was not expanding to match the width of the container. It seemed like the padding had to be manually adjusted to fit properly. Below is the code sn ...

Having trouble with loading JSON due to a Cross-Domain AJAX problem

I am facing a challenge with a URL that I do not have control over, which returns a JSON string. Within this JSON string, there is a URL that I am attempting to load using JavaScript/jQuery AJAX. However, I am encountering a Cross-Domain issue while trying ...

Is there an issue preventing Three.js from rendering properly?

I created a simple file to experiment with the Three.js library, however I encountered an issue when trying to add the renderer (an element) to the $container (which is defined as $('#container')). <!DOCTYPE html> <html lang='en&ap ...

Unspecified variable in AngularJS data binding with Onsen UI

I am new to Onsen UI and AngularJS, and I have a simple question about data binding. When I use the variable $scope.name with ng-model in an Onsen UI template, it returns as UNDEFINED. Here is my code: <!doctype html> <html lang="en" ng-app="simp ...

The directional rotation plugins of GSAP are incompatible with the plugins of PIXI

I've been experimenting with using directional rotation plugins in combination with pixi.js plugins. Unfortunately, I'm encountering some issues. If you check out the codepen link: https://codepen.io/asiankingofwhales/pen/RyNKBR?editors=0010, yo ...

What is the best way to convert this jQuery code into an AngularJS implementation?

I'm diving into the world of AngularJS and looking for a more elegant solution using AngularJS principles Controller $scope.filter = function($event, active, id) { var html = ""; if(active){ $http({method: 'GET& ...