Using v-if within v-for to showcase a list of items in a dual-column format

I apologize for reiterating this, as I have noticed that many questions similar to mine have been asked multiple times. Here are the ones that almost helped me and why they fell short:

  • This specific question advised against performing calculations within the rendering process but suggested utilizing methods/computed properties instead, which was not applicable in my case.
  • This inquiry proposed using two distinct templates with v-if, but in my scenario where the templates were nearly identical, it seemed impractical.
  • This Medium article tackled a similar issue to mine involving user filtering dealt with through computed properties, while what I sought was an if-clause to insert code snippets at specific iterations.

The Issue

I am fetching a list of items from an API, meaning the quantity will fluctuate. I aim to display them in two columns arranged like so:

-----------------
| Item1   Item5 |
| Item2   Item6 |
| Item3   Item7 |
| Item4         |
-----------------

To iterate through them, I am using a v-for loop.

My Attempts

  1. Utilizing pure CSS with display: flex

This approach yielded the following layout:

-----------------
| Item1   Item2 |
| Item3   Item4 |
| Item5   Item6 |
| Item7         |
-----------------
  1. Implementing CSS with column-count: 2;

However, this method resulted in column breaks occurring mid-element, irrespective of applying

display: block; overflow: hidden;
and other attempts. Additionally, it should be noted that the element heights can vary.

  1. Hence, I abandoned the idea of resolving it via CSS.

If this were done in php, I would simply do something like this:

<?php
if( $index == count( $items)/2 ):
  echo '</div>';
  echo '</div>';
  echo '<div class="col-md-6">';
  echo '<div class="item-container">';
endif;
?>

... However, since it's not PHP, I seek a Vue.js alternative. I attempted this:

{{#if key === Number( items.length / 2 ) }}
  </div>
  </div>
  <div class="col-md-6">
  <div class="item-container">
{{/if}

Regrettably, it didn't work, and it appears to deviate from 'the Vue way' of accomplishing such tasks. I'm struggling to discern the correct approach. :-/

Is there a recommended solution for this?

A Simplification of My Current Code

<div class="col-md-12">
    <div class="items-container">
        <div class="item-container" v-for="item, key in items['data']">
            <!-- A BUNCH OF ITEM-INFO -->
        </div>
    </div>
</div>

Answer №1

A strategy to consider would be creating a computed property that divides the items array into the appropriate number of columns.

Check out this example utilizing a flexbox layout and an additional column element.

new Vue({
  el: 'main',
  data: {
    items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6', 'Item 7'],
    cols: 2
  },
  computed: {
    columns () {
      let columns = []
      let mid = Math.ceil(this.items.length / this.cols)
      for (let col = 0; col < this.cols; col++) {
        columns.push(this.items.slice(col * mid, col * mid + mid))
      }
      return columns
    }
  }
})
.container {
  display: flex;
  border: 1px solid;
}
.col {
  margin: 10px;
  border: 1px solid;
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}
.item-container {
  border: 1px solid;
  padding: 5px;
  margin: 5px;
}
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5620233316647863786761">[email protected]</a>/dist/vue.min.js"></script>
<main>
<p><label>Columns:<label> <input type="number" v-model="cols"></p>
<div class="container">
  <div class="col" v-for="column in columns">
    <div class="item-container" v-for="item in column">{{item}}</div>
  </div>
</div>
</main>

If you're interested in a more concise way to chunk the items array, refer to Split array into chunks

Answer №2

It's great to find someone who encountered the same issue as me. I had the task of arranging 6 items in each column, so I decided to split the API response into columns and display them accordingly.

let allCategory = response.body.Categories.slice(); //clone
while (allCategory.length > 0) {
  let chunk = allCategory.splice(0,6);
  this.ColArray.push(chunk);
}  

ColArray is an array that will hold arrays representing the columns. The structure would be like:

{
  ColArray: [
    Column1: [
      Item1,
      Item2,
      Item3,
    ],
    Column2: [
      ...
    ]
  ]
}

In Vue, you can easily loop through it using:

<div v-for="(col,colIndex) in ColArray" :key="'cate_col'+colIndex" class="col-md-2">
  <div v-for="(row,rowIndex ) in col"   :key="'cate_row'+colIndex + rowIndex" class="row">
    {{row}}
  </div>
</div>

Check out this sample fiddle for a demonstration :

https://jsfiddle.net/keysl183/50wL7mdz/775484/

Answer №3

If you're looking to achieve a similar outcome of dividing items into columns but with an object instead of an array, this solution offers a way to do so.

Inspired by Phil's chosen answer, this approach utilizes Object.keys to iterate through the object and Object.entries to convert the object into an array. Instead of referencing elements by index in your HTML template, you can now access them by their corresponding keys.

new Vue({
    el: 'main',
    data: {
        items: { 
          item1: { name: 'Item 1' }, 
          item2: { name: 'Item 2' }, 
          item3: { name: 'Item 3' },
          item4: { name: 'Item 4' },
          item5: { name: 'Item 5' },
          item6: { name: 'Item 6' },
          item7: { name: 'Item 7' },
          item8: { name: 'Item 8' },
          item9: { name: 'Item 9' },
          item10: { name: 'Item 10' },
        },
        cols: 2
    },
    computed: {
          columns: function() {
              let columns = [];
              let mid = Math.ceil(Object.keys(this.items).length / this.cols);

              for (let col = 0; col < this.cols; col++) {
                  columns.push(Object.entries(this.items).slice(col * mid, col * mid + mid).map(entry => entry[1]));
              }
              return columns;
          }
      }
});
.container {
  display: flex;
  border: 1px solid;
}
.col {
  margin: 10px;
  border: 1px solid;
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}
.item-container {
  border: 1px solid;
  padding: 5px;
  margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6412110124564a514a5553">[email protected]</a>/dist/vue.min.js"></script>
  <main>
    <p><label>Columns:<label> <input type="number" v-model="cols"></p>
    <div class="container">
      <div class="col" v-for="column in columns">
        <div class="item-container" v-for="(item, index) in Object.keys(column)">
        {{column[item].name}}</div>
      </div>
    </div>
</main>

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

Guide to Increasing Table Index within Nested Loop in Vue.js 3

How can I increment table indices within a nested loop in Vue.js 3? I am currently working with two-level deep arrays using Vue.js, and I need an index that starts at 1 and increases for each item in the top-level array. The desired output is as follows: ...

Transfer information to a different element and have the ability to make changes

Having trouble displaying text from above in a textarea when trying to update it by clicking on the edit button. Any advice on how to solve this issue? enter image description here I attempted to address this with the following code, but it doesn't ...

Looking to swap an image on mouseover using vue.js?

With Vue.js, I can link an image to an img tag using this syntax: <img v-bind:src="myimage" /> Is there a way to specify a different image that will show up when hovering over the original image using Vue.js? ...

Creating an intricate table layout using AngularJS and the ngRepeat directive

I'm attempting to create a table similar to the one shown in the image below using HTML5. https://i.sstatic.net/DiPaa.png In this table, there is a multi-dimensional matrix with Class A and Class B highlighted in yellow. Each class has three modes ( ...

I am currently attempting to design a blurred background with text overlay, where the text seems to clear up the blur effect of the background. However, I am facing

Goal: I want to achieve a visually appealing effect by overlaying text on a blurred background image, making it appear as though the text is transparent and revealing the clear background image behind it. Status: I have successfully created a layout with ...

Select the element to emphasize it and reduce the visibility of others

I'm currently facing an issue with a block that contains multiple divs representing products. My goal is to have the selected product highlighted while the rest appear less prominent when clicked. I've managed to achieve this functionality, howev ...

What is the outcome of using VueUse.useMouse() if it is not made reactive?

Whenever I use VueUse.useMouse(), the results in console.log() change as I move the mouse. However, the results within <span></span> always remain 0. This is quite confusing to me, can someone please explain why this is happening? ...

Retrieve data from a JSON file URL using a GET request and save the response to be accessed globally when the Vue.js application is initialized

Consider this scenario - I have a Vue.js component where I need to display the name of a user based on their ID. The only information I have is the user's ID. However, I also have a JSON file URL that contains all the user objects with their names and ...

What is the complete list of features that ColorTranslator.FromHtml() supports and what are the reasons behind its departure from traditional CSS color interpretation guidelines

I'm looking to enhance an API by providing users with the ability to specify the color of something. I want it to accept various representations of colors, such as hex codes and CSS colors. Initially, I thought that ColorTranslator.FromHtml() would fu ...

Utilize the identical mathematical equation across various jQuery values

I have a JavaScript code that calculates the size of circles on the screen based on multiple variables. Currently, I am repeating the same equation for each circle by numbering all the variables accordingly. Is there a way to simplify this process and run ...

Error 403 with Firefox on Font Loading for CodeIgniter Website

Utilizing font-face in CSS to integrate custom fonts onto a web application being developed with CodeIgniter. This is the CSS code: @font-face { font-family: 'UniversLT-UltraCondensed'; src: url('../Fonts/LTUnivers/universlt59ultrac ...

IDGenerator Vuex

One of the challenges I'm facing is creating unique identifiers for my components. I've been considering storing a global variable in Vuex, like lastGeneratedID: 0, along with a mutation to increment this value. The current solution involves co ...

Position the element at the bottom of the page, rather than at the bottom of the

A basic example of HTML code: <!doctype html> <html> <body> <p>Some text content</p> <p>Some more content</p> <p> ... </p> <img src="image.jpg"> </body> </html> The image is meant t ...

Encountering issues while trying to run Nuxt locally with HTTPS due to a problem with the n

Currently, my objective is to run nuxt locally with HTTPS in order to test some geolocation functionalities. (https://nuxtjs.org/, https://nuxtjs.org/api/nuxt) I have been following a tutorial available at: In addition, I came across this resource as wel ...

Adjusting the spacing between the logo and navbar in HTML code

I am struggling to understand why this issue keeps occurring: I have thoroughly checked for errors, attempted multiple solutions, yet the problem persists. Please assist body { margin: 0; padding: 0; } .logo { margin: 0; padding: 0; } ul { li ...

What is the proper way to retrieve the Nuxt context within the fetch() hook?

Is there a way to access the props within an async fetch() function when also using async fetch(context)? I'm trying to figure out how to work with both simultaneously. ...

Tips for showcasing two pieces of content next to each other across the entire webpage using a combination of Bootstrap, CSS, and Laravel

I am facing a challenge with displaying two contents side by side on a full page and stacking them vertically when the screen size is small (mobile). I have managed to achieve the side by side display on a full page, but they stack on top of each other on ...

Nuxt3 encountered a request error for an unhandled 500 fetch failure at the http://localhost:3000/__nuxt_vite_node__/manifest url

Can you help me understand this error? https://i.stack.imgur.com/0FGa4.png Without making any adjustments to my code, the app builds successfully. However, when running it on dev mode, the following error appears: npm run dev I have attempted rebuilding ...

Display checkboxes on all TableRow elements as soon as one of them is checked

I've incorporated a material-ui Table into my project and have successfully implemented multi-select functionality. Here are the requirements I've fulfilled so far: Checkboxes are initially hidden - COMPLETED Hovering over a row reveals its che ...

Generating forms dynamically with Vuetify

When working with HTML code, the structure looks like this: <v-col cols="12" md="4" v-for="(leadObj, i) in lead.data" :key="i" > <v-col v-if="leadObj.inputType !=='select'"> ...