What is the best way to link a dynamic property with a bootstrap popover within a nested v-for loop?

After spending several days searching for an example to guide me without success, I am turning to SO with my specific use case.

I am currently working on a Bootstrap Vue layout where I need to load dates into 12 buttons corresponding to months and display a popover above each button with the matching dates from Firebase. I have managed to achieve almost the desired behavior by using a v-for loop to iterate through an array of objects containing month names and arrays of matching dates. Each button is laid out based on the month name, with another v-for loop inside for the popover to load all the Firebase dates.

In addition, I have a computed property that shortens the incoming Firebase dates to their abbreviated month names and then uses nested loops to match the shortened months to the existing arrays in my months array of objects. For clarity, I will include images below:

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

https://i.sstatic.net/4M7dT.jpg <-- Each button contains a popover like this, I want to matched the months in the popover to the right month button and only that one

My current challenge lies in connecting my computed property to the nested v-for loop of the popover so that only the matching dates show up for each month. Initially, I attempted to move the v-for to the button, which resulted in multiple buttons being created for each incoming date and each month name in the first v-for loop. Most solutions I've found for nested v-fors involve data coming from a single array, whereas in my case, I have separate data in separate arrays that I want to match (e.g., January button should only have January dates). Below are the relevant code blocks I currently have:

From the template:

<template>
  <b-container>
    <b-row>
        <div v-for="(month,index) in months" :key="index">
            <b-button 
                :id="`date-popover-${month.name}`"
                class="shadeCircleColor" 
                :to="{ name: 'PaymentsDetailPage', params: { id: id } }"
            >
                {{month.name}}
            </b-button>
            <b-popover
                :target="`date-popover-${month.name}`"
                triggers="hover"
                placement="top"
            >
              <div v-for="(payment, index) in payments" :key="index">
                {{payment.createdOn}}
              </div>
            </b-popover>
        </div>
        {{ matchedMonths }}
    </b-row>
  </b-container>
</template>

From the data():

data() {
    return {
      dates: [], <-- dates from firebase will show here in from a property  i.e(dates.createdOn)
      months: [ 
        {name: 'Jan', createdOn: [] }, 
        {name: 'Feb', createdOn: [] }, 
        {name: 'Mar', createdOn: [] }, 
        {name: 'Apr', createdOn: [] }, 
        {name: 'May', createdOn: [] }, 
        {name: 'Jun', createdOn: [] }, 
        {name: 'Jul', createdOn: [] }, 
        {name: 'Aug', createdOn: [] }, 
        {name: 'Sep', createdOn: [] }, 
        {name: 'Oct', createdOn: [] }, 
        {name: 'Nov', createdOn: [] }, 
        {name: 'Dec', createdOn: [] }, 
      ],
    };
  },

From the computed property:

computed: {
   matchedMonths() {
    for(let i in this.months) {
     for(let j in this.dates) {
      var dates = new Date(this.dates[j].createdOn)
      const shortMonth = dates.toLocaleString('default', { month: 'short' });
      if(shortMonth === this.months[i].name) {
        this.months[i].createdOn.push(shortMonth)
        console.log(this.months[i].name, this.months[i].createdOn)
      }
     }
    }
   }
  },

At this point, I am struggling to display the matched months properly in the popover's v-for loop so that each month shows only its respective dates. One possible issue could be with my nested for-in loops, as attempting to add the shortened months to the months.createdOn array causes all buttons to disappear. I have also tried using the .map function on the dates array and matching it to the months array, but encountered undefined errors when pushing those dates in. Here is the code illustrating these attempts:

const justDates = this.payments.map(payment => {
  return new Date(payment.createdOn).toLocaleString('default', { month: 'short'})
})
console.log(justDates)
const months= this.months.map(month => {
  if(justDates === month.name){
    return month.createdOn.push(justDates)
  }
})
console.log(months)

I am unsure about where to place the matchedMonths computed property for it to work correctly, or if using a v-if statement within either v-for loop to check if the month.name matches the shortened month name of the incoming dates would be a better approach. Any guidance on achieving the desired solution is appreciated.

Additionally, I need assistance with converting the shortened month dates back into their full month, day, and year string once the correct shortened months are matched to their buttons. Should this conversion logic also reside within the computed property? Thank you for your time.

Answer №1

Dealing with date and time can be quite tricky as bugs tend to surface unexpectedly in this area. It's best to use a reliable date handling library like dayjs, which has already addressed many common and edge cases.

If your goal is simply to sort date strings based on the month, you can do so easily:

const dates = [
  '2021-10-04',
  '2021-09-08',
  '2021-08-06',
  '2021-07-02',
  '2021-05-04',
  '2021-01-20',
  '2021-02-11',
  '2021-03-14',
  '2021-04-10',
  '2021-06-15',
  '2021-11-16',
  '2021-12-28',
]

const getMonthFromDate = (s) => {
  // Remember that Date.prototype.getMonth() counts January as 0!
  return new Date(s).getMonth()
}

const mappedMonths = dates.map(getMonthFromDate)

console.log(mappedMonths)

If the data from Firebase can be parsed by new Date(), you're halfway there - you'll have the month represented as a number from 0 to 11. If not, consider using dayjs to specify the expected date format.

Here's a snippet to help you implement this:

Vue.component('ButtonWithPopover', {
  props: ['item'],
  computed: {
    targetId() {
      return `date-popover-${this.item.name}`
    },
  },
  template: `
    <div>
      <b-button
        :id="targetId"
      >
        {{ item.name }}
      </b-button>
      <b-popover
        :target="targetId"
        triggers="hover"
        placement="bottom"
      >
        <template #title>
          {{ item.name }}:
        </template>
        <div
          v-for="createdOnItem in item.createdOn"
          :key="createdOnItem"
        >
          {{ createdOnItem }}
        </div>
      </b-popover>
    </div>
  `
})

new Vue({
  el: "#app",
  data() {
    return {
      dates: [
        '2021-10-04',
        '2021-09-08',
        '2021-08-06',
        '2021-07-02',
        '2021-05-04',
        '2021-01-20',
        '2021-02-11',
        '2021-03-14',
        '2021-04-10',
        '2021-06-15',
        '2021-11-16',
        '2021-12-28',
        '2021-02-03', 
      ],
      months: [{
          name: 'Jan',
        },
        {
          name: 'Feb',
        },
        {
          name: 'Mar',
        },
        {
          name: 'Apr',
        },
        {
          name: 'May',
        },
        {
          name: 'Jun',
        },
        {
          name: 'Jul',
        },
        {
          name: 'Aug',
        },
        {
          name: 'Sep',
        },
        {
          name: 'Oct',
        },
        {
          name: 'Nov',
        },
        {
          name: 'Dec',
        },
      ],
    }
  },
  computed: {
    monthItems() {
      return this.months.map((e, i) => {
        const createdOn = this.getFilteredDate(i, this.dates)
        return {
          ...e,
          createdOn,
        }
      })
    },
  },
  methods: {
    getMonthFromDate(date) {
      return new Date(date).getMonth()
    },
    getFilteredDate(idx, dates) {
      return dates.filter(date => {
        return this.getMonthFromDate(date) === idx
      }) || []
    },
  },
  template: `
    <b-container
      class="py-2"
    >
      <b-row>
        <b-col
          class="d-flex"
        >
          <button-with-popover
            v-for="item in monthItems"
            :key="item.name"
            :item="item"
          />
        </b-col>
      </b-row>
    </b-container>
  `
})
<!-- Include these scripts in the <head> section -->

<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.css" />

<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver" crossorigin="anonymous"></script>

<script src="//unpkg.com/vue@latest/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>

<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue-icons.min.js"></script>

<div id="app"></div>

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

Exploring the connections among nodes in a Force-Directed Graph using D3.js JavaScript

I am currently working on a complex graph that consists of around 150 nodes using D3 Javascript. The concept behind it is quite intriguing: Each node is interconnected with 100 other nodes. Out of these 100 nodes, 49 are further linked to another set of ...

Getting a `undefined` value when changing templates in VueJS

Currently, I am transitioning a list to a table to enable more columns in the future, but I have encountered an undefined variable error that has left me confused. Below is the original list: <ul class="collection with-header"> ...

The mui-datatables demo fails to display the code snippet as intended

Just diving into React and attempting to grasp MUI-datatables. The code snippet from the Codebox provided on the library's page isn't displaying in my browser, resulting in an empty page. Surprisingly, the console isn't showing any errors. ...

Is it feasible to combine various front-end applications - one developed in Vue and another in React - using just one Laravel backend?

Recently, I have taken over a laravel project that includes a vue application integrated within it (located inside resources/assets/js) using laravel-mix. My task now is to implement some front-end features with react. I am curious if it's possible to ...

can you explain which documents outline the parameters that are passed into the express app.METHOD callback function?

As a beginner in javascript and nodejs, I often struggle with understanding callback functions. One thing that particularly confuses me is determining what arguments are passed into a callback function. Let's consider the following example: app.get( ...

Troubleshooting error messages in the console during conversion of image URL to Base64 in AngularJS

While attempting to convert an image URL to Base64 using the FromImageUrl method, I encountered an error in my console. Access to the image located at '' from the origin 'http://localhost:8383' has been blocked due to CORS policy ...

Solution: "The Mongoose Model.find method consistently provides an empty result set."

Currently, I am utilizing mongoose in combination with next.js to interact with an existing collection. Despite the collection not being empty, the mongoose model.find() function consistently returns an empty array. Below is my model code: const mongoose ...

Exporting Canvas data without the alpha channel

I am looking for a way to resize images that are uploaded in the browser. Currently, I am utilizing canvas to draw the image and then resize it using the result from the toDataURL method. A simplified version of the code (without the upload section) looks ...

During multiple-array references, what action is the dereference operator instructing the compiler to take?

Recently, I've been experimenting with multidimensional arrays and bracket notation in an attempt to grasp the relationship between dereferencing, pointer type, and pointer arithmetic. Let's focus on a hypothetical 3D array reference for this di ...

Implementing a customized login form with Django UserForm and Bootstrap 4 styling

Hey there! I'm trying to integrate Bootstrap's login view into my UserForm but have encountered some issues. Here is the template I'm using: <body class="text-center"> <form class="form-signin" action="" method="post"> {% c ...

Switching between sockets on a rotational basis

Imagine there is a division in the HTML file, and an interesting game is being played where each player has the opportunity to change the color. However, there’s a twist – they can only switch colors after the other player has taken their turn. The pla ...

execute function when loading a page on nuxt framework

Can Nuxt middleware be modified to execute after a page has been mounted? If the following middleware is used: export default function ({ app }) { if (!process.server) { app.$somePluginFunction() } } It currently triggers when navigating to a pag ...

Explore the functionalities of Pinia Vue for querying data with a filter

My objective is to attract customers based on the groups ID that has been provided. Here is the current status of the customers: [ { "id": 1, "name": "Customer 1", "groups": [ { "id& ...

How to implement a cyclic item generation feature in React.js

My function is meant to draw multiple items in a cycle, but it is only drawing the first item when I attempt to draw 5. This is my function: export default function CinemaHole() { const items = []; for(let i = 0; i < 5; i++) { item ...

What causes the event parameter to be lost during recursion?

Below is the given code snippet: <html> <head> <meta charset="UTF-8"> <title>title...</title> <link type="text/css" rel="stylesheet" href="jquery-ui-1.10.4.custom.css" /> <script type="text/javascript" src="jq ...

Filtering data in AngularJS by parsing JSON records

I have a JSON file containing restaurant information and I need to display the data by grouping them based on their respective address fields. For example, all restaurants with the address 'Delhi' should be shown first, followed by those from &ap ...

What is the process for retrieving an image from firebase storage?

I need some help accessing an image that I uploaded to Firebase storage. I followed the instructions provided in https://firebase.google.com/docs/storage/web/download-files but unfortunately, it's not working for me. My goal is quite simple: I have a ...

"Incorporate countless Bootstrap 4 carousels into one webpage for an enhanced user

I'm experiencing difficulties with the new Bootstrap 4. Can anyone provide guidance on how to incorporate multiple Bootstrap carousels into a single page? I've researched several websites, but most only offer solutions for Bootstrap 3. Your assi ...

Issues with clearRect() function in HTML5 canvas

Recently, I developed a function with the goal of redrawing a square line block that covers the entire page whenever the window size is adjusted. For reference, you can view my code at http://jsfiddle.net/9hVnZ/ However, I encountered an issue: bgCtx.cl ...

Retrieving data from a method's return statement with an array

Is there a safe way to access both array values from another class method without triggering the nullexception warning? I am successfully retrieving both values but concerned about the "Array access x[0] may produce NPE" warning without using intents p ...