Achieve a stylish layout by displaying three cards per row using Vue.js and Bootstrap 4

I'm facing an issue with my code where I am trying to display Bootstrap cards in rows of three, but the layout is not rendering correctly. It starts with one card, then two, and eventually adds a new column, resulting in some empty spaces with offsets.

Any assistance with Vue or CSS would be greatly appreciated. I believe the solution may involve only printing the div elements with classes row and col when index mod 3 = 0, but I'm uncertain how to implement this using Vue.

HTML

<div id="app">
  <div class="row">
    <div class="col-md-12">
      <h1 class="text-center">Products</h1>
      <hr />
    </div>
  </div>

  <img v-if="loading" src="https://i.imgur.com/JfPpwOA.gif " />

  <section v-else style="margin-left: 10px;">
    <div v-for="(product, index) in products.slice(0, 15)">
      <!-- if we are 3 cards wide start a new row -->
      <div :class="{'row':(index % 3 === 0)}">
        <div :class="{'col-md-12':(index % 3 === 0)}">
          <div class="card" style="width: 16rem; float:left;">
            <img class="card-img-top" :src="product.thumbnailUrl" alt="card image collar">
            <div class="card-body">
              <h5 class="card-title">Product {{index}}</h5>
              <p class="card-text">Product {{index}} - {{product.title}}</p>
              <button v-on:click="addProductToCart(product)" class="btn btn-primary">Add To Cart</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</div>

JS

// JavaScript

var prodData = [
   ...

  ]

new Vue({
  el: "#app",
  data() {
    return {
      loading: false,
      cart: []
    }
  },
  methods: {
    addProductToCart: function(product) {
      this.cart.push(product);
      console.log(this.cart);
    }
  },
  created() {
    this.loading = true;
        this.products = prodData;
    this.loading = false;

  }
})

FIDDLE

Answer №1

If you want to repeat the columns (excluding the .row), you can easily achieve this using Vue's v-for...

To apply the v-for, make sure it is on the column itself. Utilize .col-md-4 to display 3 cards per row as specified by Bootstrap, which will automatically wrap the columns without requiring additional repeated row divs...

Check out the code snippet and demonstration here: https://jsfiddle.net/vbpb4me5/

<div class="row">
    <div class="col-md-4" v-for="(product, index) in products.slice(0, 15)">
          <div class="card h-100">
            <img class="card-img-top" :src="product.thumbnailUrl" alt="card image collar">
            <div class="card-body">
              <h5 class="card-title">Product {{index}}</h5>
              <p class="card-text">Product {{index}} - {{product.title}}</p>
              <button v-on:click="addProductToCart(product)" class="btn btn-primary">Add To Cart</button>
            </div>
          </div>
     </div>
 </div>

This implementation ensures a correct grid structure (row>col). Remember to place the row within a container or container-fluid to avoid horizontal scrollbars.

For a hands-on demo, visit: Codeply Demo


Further reading on creating three columns of checkboxes in Bootstrap 4 with Vue: Vue three columns of checkboxes in Bootstrap 4

Answer №2

Your current code is producing an unexpected output, such as:

<div>
  <div class="row">Your card</div>
  <div class="">Your card</div>
  <div class="">Your card</div>
  <div class="row">Your card</div>
</div>

One possible solution is to convert your 1D array to a 2D array and then use nested loops to achieve the desired result.

Here is a function that can help you convert a 1D array to a 2D array:

get2DArrary: function(arr) {
  let newArr = [];
  while(arr.length) newArr.push(arr.splice(0,3))
  return newArr
}

Below is a sample implementation:

var prodData = [
  // Sample product data here...
];

new Vue({
  el: "#app",
  data() {
    return {
      loading: false,
      cart: []
    }
  },
  methods: {
    addProductToCart: function(product) {
      this.cart.push(product);
      console.log(this.cart);
    },
    get2DArrary: function(arr) {
      let newArr = [];
      while(arr.length) newArr.push(arr.splice(0,3))
      return newArr
    }
  },
  created() {
    this.loading = true;
    this.products = prodData;
    this.loading = false;
      
  }
})
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 <div id="app">
  <div class="row">
    <div class="col-md-12">
      <h1 class="text-center">Products</h1>
      <hr />
    </div>
  </div>

    
  <img v-if="loading" src="https://i.imgur.com/JfPpwOA.gif" />
    
  <section v-else style="margin-left: 10px;">
    <div v-for="(row, rowIndex) in get2DArrary(products.slice(0, 15))" :key="rowIndex" class="row">
    <div v-for="(product, productIndex) in row" :key="productIndex" class="col-md-4">
        <div>
          <div class="card" style="width: 16rem; float:left;">
            <img class="card-img-top" :src="product.thumbnailUrl" alt="card image collar">
            <div class="card-body">
              <h5 class="card-title">Product {{index}}</h5>
              <p class="card-text">Product {{index}} - {{product.title}}</p>
              <button v-on:click="addProductToCart(product)" class="btn btn-primary">Add To Cart</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</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

What might be causing my direct descendant selector to not work as expected?

I'm struggling with my direct descendant selector. Let's analyze a straightforward example: .myDiv > table tr:first-child td { font-weight: bold; text-align: center; } <div class="myDiv"> <table style="width:100%"> < ...

Using Vuex to handle form data submissions in VueJS

As a beginner in vuex and vue-js, I'm looking to utilize vuex for posting form data. While I've successfully passed the Title so far, I'm unsure about how to pass multiple pieces of data. Data to be passed: // Title <input type="text" n ...

Having trouble with Vue3 ref not updating?

Can someone help me understand how refs work? I feel like I'm missing a simple mistake, but it's just not clicking for me. Could someone please explain? I have props passed from the outer component in the script setup: const props = defineProps( ...

Performance problems arising from use of Tailwind CSS drop-shadow class

Lately, I've been struggling to pinpoint the root cause of a drastic FPS drop in my application. The issue arises after using the app for some time, leading to a performance plunge down to around 10FPS. After investigating, I discovered that eliminati ...

Is there a way to generate a list using Vuejs from this multidimensional array?

Hey there, I have a question regarding my axios request which returns an array structured like the one below: Here is my axios code: axios.post('/api/hr_employee/days/'+ this.period_data.year +'/'+ this.period_data.month +'?page=& ...

Troubleshooting problem with CSS borders in Microsoft Edge Browser

I am currently working on implementing the concept found at the following link: https://www.w3schools.com/howto/howto_js_tabs.asp, but with the additional feature of borders around the elements. However, I have encountered an issue specifically with Micro ...

Creating a menu bar in jQuery that functions similarly to tabs, but without the need to change divs

Currently, I am exploring ways to create tab functionality similar to jQuery's tabs plugin but without the need to switch between DIVs. Essentially, I am looking for a jQuery plugin that solely handles rendering the tab bar and allows me to designate ...

Is there a way to stop objects in a particular list from being dragged into another nested ul using jquery sortable?

I am faced with a challenge regarding nested lists, specifically when it comes to preventing parent-parent list items from dropping into an inner <div> within an <li> <div> that already contains its own <ul> <li> list. Additio ...

What is the best way to show the page before loading custom fonts?

My website is time-sensitive and I need it to prioritize displaying the page before loading any custom fonts. Essentially, I want the page to show up initially as if the fonts are not loaded. This way, the custom fonts (if already cached by the browser) w ...

Embed a unique custom design sheet within a Facebook iframe

I'm looking to customize the design of my Facebook feed by adding a custom style sheet inside a Facebook iframe. Here's what I have: <iframe width="1000px" height="1000px" frameborder="0" name="f1e348592ed856c" allowtransparency="true" allo ...

Tips for creating a unified user interface framework for various web applications sharing a consistent design

Struggling to create a user interface framework for multiple web applications, I'm faced with the challenge of setting up the infrastructure in our unique situation: We have 7 (or more) different web applications built using asp.net mvc Some of thes ...

Facing difficulty in passing data to a child component through VueX

<template> ... <note-info></note-info> <note-list></note-list> ... </template> I am utilizing VueX and store.js export default { state: { noteDataList: [], }, mutations: { setNoteDataList: fu ...

Guide on utilizing font-awesome icons stored in node-modules

After successfully installing font-awesome 4.0.3 icons through the npm install command, How can I incorporate them into an HTML file from the node-modules directory? If edits need to be made to the less file, do they have to be done within the node-modul ...

What could be causing the Vue.js image component to malfunction?

I am having an issue. I want to create a Vue.js Component. This component displays an image, similar to the <img> tag. If you are familiar with the <img> tag, this question should be easy for you to understand. Here is the code I have: props ...

A method to ensure that inline <div> elements occupy the entire available width

The typical solution for this issue is using float, however, it does not work in my situation. I attempted to utilize flexbox and overflow:hidden for the parent element, but unfortunately, it did not resolve the issue. Currently, I am dealing with three i ...

The fill layout in Next.JS Image isn't working properly

According to the Next.js image component documentation, it states: "When fill, the image will stretch both width and height to the dimensions of the parent element, usually paired with object-fit." However, in reality, the image fills the entire ...

What could be causing the discrepancy in alignment of my hyperlink?

I noticed that in this example the hyperlink is aligned at the bottom compared to the text. Can anyone help me identify the issue and provide a solution? Thank you! ...

What is the best way to ensure the active tab remains visible?

I am using the bs-admin-bcore Bootstrap theme and I am looking to enhance the dynamic functionality of the menu in the "menu.php" file. Here is an example of the current code: <ul id="menu" class="collapse"> <li class="panel"> < ...

tips for utilizing namespaced getter filtering within a Vuex module in vueJs

In my custom module named ShopItemCategory, I have a Getter getters: { shopItemsCategories: state => state.ShopItemsCategories.data, }, Inside the component, there is a computed function I defined computed: { shopItemsCategories ...

Troubination of Bootstrap CSS is causing issues on Floating Action Menu

I've implemented a Floating Action Menu using JQuery and CSS: HTML: <script src="https://code.jquery.com/jquery-1.11.3.js"></script> <div id="hamburger" class="waves-effect waves-light"> <div id="wrapper"> <span ...