Dynamically wrap each element into a Bootstrap column div

II want to output fields from a Json file into my Products page and each element should be wrapped into a Bootstrap column for a good display. The way it is displayed now, all the items are wrapped into a single column. I should dynamically add a tag after every single product and start a new one right after it. I haven’t succeeded to come up with a valid solution yet. Someone can help me, please? Thanks. Here is my code:


//display products
class UI {
 displayProducts(products){
let output = '';
<div class="row">
      products.forEach(product =>{   
     output+= `<!-- single product -->
       <div class="col-md-4">
       <article class="product">
         <div class="img-container">
          <div class="thumbnail">
           <img src=${product.image} alt="product" class="product-img"/>
           <button class="bag-btn" data-id=${product.id}>Add to cart<i class="fas fa-cart-plus"></i></button>
           <div class="caption">
          <p>${product.title} <span>€ ${product.price}</span></p>
        </div> <!--end caption-->
         </div> <!--end thumbnail-->
         </div> <!--end img container-->
       </article>
     </div> <!--end column-->
</div> <!--end row-->
  <!-- end single product -->
` ;



}) 

productsDOM.innerHTML = output;
 }
}

First I would correctly close your comments. Starting on the line below, you have effectively commented out all the other closing tags.

        </div> <!--end caption>

Randel is right when you are writing html/csc code. you alway’s use a closing and an opening braket it’s like the yin and yang of the universe.

there are a few expections but they are very few.

Yeah, pretty dumb error, thanks for letting me know. I simply didn’t notice. Still, this doesn’t solve my problem.

If you can provide a bit more code where I can test and see what is getting displayed, that would help.

EDIT: Can you provide a screen shot of how it is currently displaying in a single column?

Thanks for the answer.

Here’s the HTML code that deals with Products:

<div class="container-fluid">
<section class="products">
  <div class="title">
    <h2>Our Products</h>
  </div>
  <div class="row">
  <div class="products-center">

    <!-- Dynamically generated products -->


</div>
<!-- Cart-->
<div class="cart-overlay">
   <div class="cart">
   <span class="close-cart">
     <i class="far fa-window-close"></i>
   </span>
   <h2>Your Cart</h2>
   <div class="cart-content">

     <!--Cart Item -->
     <div class="cart-item">

     </div>
     <!-- end Cart Item -->
     </div>
      <!-- Cart Footer -->
      <div class="cart-footer">
        <h3>Your Total : € <span class="cart-total"><strong>0</strong></span> </h3>
         <button class="clear-cart banner-btn">Clear Cart</button>
      </div>
      <!--end  Cart Footer -->



   </div>
</div>
<!-- end Cart -->
</div> <!-- end Container Fluid -->

The JSON file :

{
  "items": [
    {
      "sys": { "id": "1" },
      "fields": {
        "title": "Modern Bed With Lights",
        "price": 322,
        "image": { "fields": { "file": { "url": "img/1.jpg" } } }
      }
    },
    {
      "sys": { "id": "2" },
      "fields": {
        "title": "Palermo modern white-platform bed",
        "price": 411,
        "image": { "fields": { "file": { "url": "img/2.jpg" } } }
      }
    },
    {
      "sys": { "id": "3" },
      "fields": {
        "title": "Modern Platform bed casual cottage",
        "price": 623,
        "image": { "fields": { "file": { "url": "img/3.jpg" } } }
      }
    },
    {
      "sys": { "id": "4" },
      "fields": {
        "title": "Prince bed modloft-cressina",
        "price": 722,
        "image": { "fields": { "file": { "url": "img/4.jpg"} } }
      }
    },
    {
      "sys": { "id": "5" },
      "fields": {
        "title": "Barcelona White Modern Bed Frame",
        "price": 425,
        "image": { "fields": { "file": { "url": "img/5.jpg" } } }
      }
    },
    {
      "sys": { "id": "6" },
      "fields": {
        "title": "Dream Queen Size Bed",
        "price": 999,
        "image": { "fields": { "file": { "url": "img/5.jpg" } } }
      }
    },
    {
      "sys": { "id": "7" },
      "fields": {
        "title": "Modern Sofa Set Designs For Living Room ",
        "price": 234,
        "image": { "fields": { "file": { "url": "img/7.jpg" } } }
      }
    },
    {
      "sys": { "id": "8" },
      "fields": {
        "title": "Modern sofa set",
        "price": 214,
        "image": { "fields": { "file": { "url": "img/8.jpg" } } }
      }

    },
      {
        "sys": { "id": "9" },
        "fields": {
          "title": "Plastic Modern Sofa Set",
          "price": 124,
          "image": { "fields": { "file": { "url": "img/9.jpg" } } }
        }
    }
  ]
}

And the JS file. It’s still in working process. I’m interested especially in the UI class , where the products are dynamically displayed on page:

//variables
const cartBtn = document.querySelector('.cart-btn');
const closeCartBtn = document.querySelector('.close-cart');
const clearCartBtn = document.querySelector('.clear-cart');
const cartDOM = document.querySelector('.cart');
const cartOverlay = document.querySelector('.cart-overlay');
const cartItems = document.querySelector('.cart-items');
const cartTotal = document.querySelector('.cart-total');
const cartContent = document.querySelector('.cart-content');
const productsDOM = document.querySelector('.products-center');


//CART
let cart = [];
//buttons
let btnsDom =[];

//get the products
class Products{

async getProducts() {

  try{
  let result = await fetch("http://127.0.0.1:8887/products.json");
  let data= await result.json();    
  let products = data.items; 
  products = products.map(item =>{   
  const {title,price} = item.fields; 
  const {id} = item.sys; 
  const image = item.fields.image.fields.file.url; 
  return {title,price,id,image}; 
}) //end items map

  return products;

} //end try
 catch (error){
 console.log(error);
}// end catch

} //end get Products
}  //end PRODUCTS class


//UI class
class UI {
 displayProducts(products){
let output = ''';

      products.forEach(product =>{
      
     output+= `<!-- single product -->

       <article class="product">
         <div class="img-container">
          <div class="thumbnail">
           <img src=${product.image} alt="product" class="product-img"/>
           <button class="bag-btn" data-id=${product.id}>Add to cart<i class="fas fa-cart-plus"></i></button>
           <div class="caption">
          <p>${product.title} <span>€ ${product.price}</span></p>
        </div> <!--end caption-->
         </div> <!--end thumbnail-->
         </div> <!--end img container-->
       </article>

  <!--end single product-->
`;

}) //end of forEach

productsDOM.innerHTML = output ;


} //end of displayProducts()


 getCartBtns(){
  const btns = [...document.querySelectorAll('.bag-btn')]; 
  btnsDom = btns;
  btns.forEach(button =>{  
  let id = button.dataset.id;  
  let inCart = cart.find(item => item.id === id)  
  
  if (inCart){
    button.innerText = "In Cart"; 
    button.disabled = true; 
    Storage.saveProducts(products);
  } //end IF

    button.addEventListener('click', event =>{
    event.target.innerText = "inCart";
    event.target.disabled = true;
  
     let cartItem = {...Storage.getProduct(id), amount:1}; 


    
    cart = [...cart, cartItem]; 

    //save cart in local storage
    Storage.saveCart(cart);
    console.log(cart);

    //set cart values
    this.setCartValues(cart) 

    //display cart item
    this.addCartItem(cartItem); 

    // show the cart 
    this.showCart();

 }); //end of forEach button

}); //end EVENT Listener

}

  setCartValues(cart){
    let tempTotal = 0; //cart total
    let itemsTotal = 0; //items total amount
    //map method
    cart.map(item =>{
      tempTotal+= item.price * item.amount;
      itemsTotal+= item.amount;

    }); //end cart MAP

    //display values
    cartTotal.innerText = parseFloat(tempTotal.toFixed(2)) 
    cartItems.innerText = itemsTotal;

  } //end setCartValues

//method to add items in cart

  addCartItem(item){
    
const div = document.createElement('div');
 div.classList.add('class-item');
 div.innerHTML = ` <img src=${item.image} alt="product"/>
 <div>
   <h4>${item.title}</h4>
   <h5>€ ${item.price}</h5>
   <span class="remove-item" data-id= ${item.id}>remove</span>
 </div>
 <div class="amount"><i class="fas fa-sort-up fa-lg" data-id= ${item.id}></i>
 <p class="item-qta"><strong>${item.amount}</strong></p>
 <i class="fas fa-sort-down fa-lg" data-id= ${item.id}></i>
 </div>
 `
cartContent.appendChild(div);

  }

  showCart(){
    cartOverlay.classList.add('transparentBcg');
    cartDOM.classList.add('showCart');
  }

 } //end of getCardBtns()



//local Storage
class Storage{
  //save products in local storage
  static saveProducts(products){
    localStorage.setItem("products", JSON.stringify(products));

  }

  static getProduct(id){
    
     let products = JSON.parse(localStorage.getItem('products')); 
    
    return products.find(product => product.id === id); 
  }

  static saveCart(cart){
    localStorage.setItem("cart", JSON.stringify(cart));

  }



}

document.addEventListener("DOMContentLoaded", () => {
const ui = new UI();
const products = new Products();
//get all products
products.getProducts().then(products => {
ui.displayProducts(products); //display products on the UI
Storage.saveProducts(products); //store products in local Storage
/
}).then ( () =>{
  ui.getCartBtns();
})

});