I'm using NodeJS, Handlebars & Bootstrap to build a simple webapp. It should loop through a MongoDB collection of mock products and display it's fields.
I'm displaying the data in "product cards" (refer to image). When looping through the collection to create the cards it's reading each Product fine however, I have a button which opens a modal on each card - which should display the info related to that product, this doesn't work.
The issue is that all the modals display info related to only the first index in the MongoDB collection.
Here is my HTML code to display the products:
<div class="products">
<h1>Featured Products</h1>
<section class="product-list">
<div class="product-container">
{{#each Product}}
<div class="card">
<div class="title">{{this.name}}</div>
<div class="image">
<img src="https://source.unsplash.com/random/50×50/?fruit" />
</div>
<div class="cost">{{this.cost}}</div>
<div>
<button class="buy-button">Add to cart</button>
<button
class="buy-button"
data-toggle="modal"
data-target=".bd-example-modal-sm"
>More Detail</button>
</div>
</div>
<!-- modal -->
<div
class="modal fade bd-example-modal-sm"
data-toggle="modal"
aria-hidden="true"
>
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="carousel" data-ride="carousel">
<div class="parentSlider">
<div class="parentSlider-cell">
<img
class="img"
src="https://source.unsplash.com/random/50×50/?fruit"
/>
</div>
<div class="product-detail-text">
<p>Name: {{this.name}}</p>
<p>Price: {{this.cost}}</p>
<p>Description: {{this.description}}</p>
</div>
</div>
</div>
<div class="modal-footer">
<button class="buy-button" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{{/each}}
</div>
</section>
</div>
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 class="product-container">
{{#each Product}}
<div class="card">
<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}}"
class="modal"
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>