Home > Enterprise >  Handlebarsjs #each modal displaying only first element
Handlebarsjs #each modal displaying only first element

Time:04-09

I'm using NodeJS and HandlebarsJs in a webapp, which loops through a MongoDB collection of mock products and displays their fields.

I'm displaying the data in "product cards" (refer to image) and when looping through the collection to create the cards, it works as intended. However I have a button to open a modal on each card which should display the info related to that product, which doesn't work.

The issue is that all the modals display info related to only the first index in the MongoDB collection. In the image you'll notice the product cards are still reading successfully as their not repeated.

I've tried rearranging the location and closing of the #each loop, and also adding a separate #each loop just for the modals, however when using the separate #each loop inside the modal, it loops through every product which isn't desired (should be only the product clicked on).

Here is my HTML code to display the products:

<div >
  <h1>Featured Products</h1>
  <section >
    <div >
      {{#each Product}}
        <div >
          <div >{{this.name}}</div>
          <div >
            <img src="https://source.unsplash.com/random/50×50/?fruit" />
          </div>
          <div >{{this.cost}}</div>
          <div>
            <button >Add to cart</button>
            <button
              
              data-toggle="modal"
              data-target=".bd-example-modal-sm"
            >More Detail</button>
          </div>
        </div>

        <!-- modal -->
        <div
          
          data-toggle="modal"
          aria-hidden="true"
        >
          <div >
            <div >
              <div  data-ride="carousel">
                <div >
                  <div >
                    <img
                      
                      src="https://source.unsplash.com/random/50×50/?fruit"
                    />
                  </div>
                  <div >
                    <p>Name: {{this.name}}</p>
                    <p>Price: {{this.cost}}</p>
                    <p>Description: {{this.description}}</p>
                  </div>
                </div>
              </div>
              
              <div >
                <button  data-dismiss="modal">Close</button>
              </div>
            </div>
          </div>
        </div>
      {{/each}}
    </div>
  </section>
</div>

CodePudding user response:

You need unique IDs for your modals. I'm assuming you are using Bootstrap for the functional part of opening the modals. The data-target attribute should be a unique selector for the modal you'd like to show.

Currently, you have this set to .bd-example-modal-sm, which will open the first element that matches that selector.

If you don't have a unique ID with your Product array, you can use the the special variable name @index to get the index of the current item you are looping over. If Product is an object, then you can use @key.

Here is a simple example with an array of Products:

const template_source = document.getElementById('template-source').innerHTML;
const template = Handlebars.compile(template_source);

const compiled_html = template({
  Product: [
    {
      name: 'Apples',
      cost: '1.00',
      description: 'Apples can be red or green.',
    },
    {
      name: 'Bananas',
      cost: '2.00',
      description: 'Bananas are usually yellow, but sometimes green or brown.',
    },
    {
      name: 'Coconuts',
      cost: '3.00',
      description: 'Coconuts when pealed are usually tan or brown.',
    },
  ]
});

const app = document.getElementById('app');
app.innerHTML = compiled_html;
@import url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.min.css');


/* misc */
.product-container {
  display: flex;
  flex-wrap: wrap;
}

.card,
.modal {
  padding: 0.5em;
  margin: 0.5em;
}

.card {
  background: gainsboro;
}

.modal {
  background: beige;
  max-width: 300px;
  max-height: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.1/js/bootstrap.min.js"></script>

<script id="template-source" type="text/x-handlebars-template">
   <div >
     {{#each Product}}
       <div >
         <p>Name: {{this.name}}</p>
         <p>Cost: {{this.cost}}</p>
         <button
           data-toggle="modal"
           data-target="#product-modal-{{@index}}"
         >
           More Detail
         </button>
       </div>
       
       <!-- modal -->
       <div
         id="product-modal-{{@index}}"
         
         data-toggle="modal"
         aria-hidden="true"
       >
         <p>Name: {{this.name}}</p>
         <p>Cost: {{this.cost}}</p>
         <p>Description: {{this.description}}</p>
         <button data-bs-dismiss="modal" data-dismiss="modal">Close</button>
       </div>
     {{/each}}
   </div>
</script>

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

  • Related