javascripthandlebars.js

Calculate the total volume in JavaScript/Handlebars program for a moving company


i'm quite new to programming and currently and having a problem with a website project. It's a website for a moving company. In this website there is a system of estimation of volume of all the furniture selected by user. I have already the system which allows to select a different number of furniture to move in different categories. Each furniture has a title, image, quantity and a increase an decrease buttons. Additionally, in my JS object each furniture have a volume parameter. I user Handlebars to display all the furniture from my JS objects. I am supposed to do a program to calculate the total volume of all the furniture selected. I have already a code to do this, but the problem is that the value of total volume can be affected by the decrease button of other furniture which is not selected. For example, if i have selected 2 TVs, and one couch, i have a total volume of 4 cubic meters, but this total volume can be decreased if i press the decrease button of washing machine which was not selected initially and still have a quantity = 0.

Here I've selected 2 couches, 1 small bed and 1 large bed by pressing the "+" buttons, and i have the correct value of the total volume..

However, here i managed to decrease the value of total volume to 0 by pressing the "-" of the third furniture.. After first click, it decreased the total volume by 1 and it turns 9, which is logic, but i can press the same button again until the total volume becomes 0, which is not logic because i still have 2 other furniture selected.

Here is my JS code (i have not written all this code by myself):

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

const contextSalon = {
    lesMeublesSalon: [
        {
            image: 'images/canape.png',
            element: 'Canapé',
            quantity: 0,
            index: 0,
            volume: 3
        },
        {
            image: 'images/canape.png',
            element: 'Lit',
            quantity: 0,
            index: 0,
            volume: 4
        },
        {
          image: 'images/bed.svg.png',
          element: 'Lit double',
          quantity: 0,
          index: 0,
          volume: 1
      }
    ]
};

const compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;

const contextChambre = {
    lesMeublesChambre: [
        {
            image: 'images/bed.svg.png',
            element: 'Lit double',
            quantity: 0,
            index: 0,
            volume: 1
        }
    ]
}

const compiledHtmlChambre = template(contextChambre);
const injectionObjetChambre = document.getElementById('meuble-chambre');
injectionObjetChambre.innerHTML = compiledHtmlChambre;

const contextCuisine = {
    lesMeublesCuisine: [
        {
            image: 'images/frigo.svg',
            element: 'Frigo',
            quantity: 0,
            index: 0,
            volume: 1
        }
    ]
}

const compiledHtmlCuisine = template(contextCuisine);
const injectionObjetCuisine = document.getElementById('meuble-cuisine');
injectionObjetCuisine.innerHTML = compiledHtmlCuisine;

const contextBain = {
    lesMeublesBain: [
        {
            image: 'images/machine-a-laver.svg',
            element: 'Machine à laver',
            quantity: 0,
            index: 0,
            volume: 1
        }
    ]
}

const compiledHtmlBain = template(contextBain);
const injectionObjetBain = document.getElementById('meuble-bain');
injectionObjetBain.innerHTML = compiledHtmlBain;

let totalVolume = 0; //use variable globale pour stocker le volume total des meubles 

function renderCategory (containerElement, products) {
    // initially render our HTML with Handlebars
    containerElement.innerHTML = template({
      products: products
    });
  
    const addOneBtns = containerElement.querySelectorAll("[data-increase]");
    const removeOneBtns = containerElement.querySelectorAll("[data-decrease]");
    const quantityDisplays = containerElement.querySelectorAll("[data-quantity]");
  
    function renderQuantities () {
      quantityDisplays.forEach((quantityDisplay, index) => {
        const quantity = products[index].quantity;
        quantityDisplay.textContent = String(quantity);
      }) 
    };

    function updateTotalVolumeDisplay() {
      const totalVolumeDisplay = document.getElementById("volume-total");
      totalVolumeDisplay.textContent = totalVolume;
    }

    addOneBtns.forEach(addOneBtn => {
        addOneBtn.addEventListener('click', function (event) {
          const index = Number(event.target.dataset.increase);
          products[index].quantity += 1;
          totalVolume += products[index].volume;
          renderQuantities();
          updateTotalVolumeDisplay();
        });
      });
  
      removeOneBtns.forEach(removeOneBtn => {
        removeOneBtn.addEventListener('click', function (event) {
          const index = Number(event.target.dataset.decrease);
          products[index].quantity = Math.max(products[index].quantity - 1, 0);
          totalVolume -= products[index].volume;
          if(totalVolume < 0){
            totalVolume = 0;
          }
          renderQuantities();
          updateTotalVolumeDisplay();
        });
      });
      
      function updateTotalVolumeDisplay() {
        const totalVolumeDisplay = document.getElementById("volume-total");
        totalVolumeDisplay.textContent = totalVolume;
      }
    
  }
  
  const salonContainer = document.getElementById('meuble-salon');
  renderCategory(salonContainer, contextSalon.lesMeublesSalon);

  const chambreContainer = document.getElementById('meuble-chambre');
  renderCategory(chambreContainer, contextChambre.lesMeublesChambre);

  const cuisineContainer = document.getElementById('meuble-cuisine');
  renderCategory(cuisineContainer, contextCuisine.lesMeublesCuisine);

  const bainContainer = document.getElementById('meuble-bain');
  renderCategory(bainContainer, contextBain.lesMeublesBain);

And here is my HTML code associated with JS and Handlebars:

<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
    <script id="templateHB" type="text/x-handlebars-template">

        {{#each products}}
        <div class="meuble"><img src="{{this.image}}">
            <p>{{this.element}}</p>
            <div class="plus-moin">
                <button data-increase="{{@index}}">+</button>
                <p data-quantity="{{@index}}">{{this.quantity}}</p>
                <button data-decrease="{{@index}}">-</button>
            </div>
        </div>
        {{/each}}
    </script>
</head>

<body>
    <header>
        <nav>
            <a href="#">
                <h1 class="logo"><span class="way">WAY</span>TRANSPORT</h1>
            </a>
            <ul>
                <a href="index.html">
                    <li>ACCUEIL</li>
                </a>
                <a href="services.html">
                    <li>SERVICES</li>
                </a>
                <a href="devis.html">
                    <li class="active">DEVIS</li>
                </a>
                <a href="#">
                    <li>TARIFS</li>
                </a>
                <a href="contact.html">
                    <li>CONTACT</li>
                </a>
            </ul>
        </nav>
    </header>

    <main>
        <div class="banniere">
            Réaliser un devis en ligne
        </div>
        <article>
            <!-- LES BOUTTONS ONGLET -->
            <button class="active" id="btn-salon">SALON</button>
            <button id="btn-chambre">CHAMBRE</button>
            <button id="btn-cuisine">CUISINE</button>
            <button class="last" id="btn-bain">SALLE DE BAIN</button>

            <!-- ONGLET POUR LES ELEMENT DU SALON -->
            <div id="onglet-salon" class="contenu">
                <h2>Seléctionnez vos meuble de salon</h2>
                <div id="meuble-salon" class="produit">
                </div>
            </div>
            <!-- ONGLET POUR LES ELEMENT DE CHAMBRE -->
            <div id="onglet-chambre" class="contenu" style="display: none;">
                <h2>Seléctionnez vos meuble de chambre</h2>
                <div id="meuble-chambre" class="produit">
                </div>
            </div>
            <!-- ONGLET POUR LES ELEMENT DE CUISINE -->
            <div id="onglet-cuisine" class="contenu" style="display: none;">
                <h2>Seléctionnez vos meuble de cuisine</h2>
                <div id="meuble-cuisine" class="produit"></div>
            </div>
            <!-- ONGLET POUR LES ELEMENT DE SALLE DE BAIN -->
            <div id="onglet-bain" class="contenu" style="display: none;">
                <h2>Seléctionnez vos meuble de bain</h2>
                <div id="meuble-bain" class="produit"></div>
            </div>
        </article>

        <section>
            <div id="tester" class="selected-total">
                <p>Produit seléctionnez :</p>
                <div class="selected">
                    <ul>
                        <li>Lit double <span class="nombre">x 1</span></li>
                        <li>Canapé <span class="nombre">x 1</span></li>
                        <li>Armoir <span class="nombre">x 2</span></li>
                    </ul>
                </div>
                <div class="selected">
                    <ul>
                        <li>Lit double <span class="nombre">x 1</span></li>
                        <li>Canapé <span class="nombre">x 1</span></li>
                        <li>Armoir <span class="nombre">x 2</span></li>
                    </ul>
                </div>
                <div class="total">
                    <p>Total m³ : <span id="volume-total">0</span></p>
                    <p>Prix : <span id="prix-total">0</span></p>
                </div>
            </div>
        </section>
    </main>

I have tried different ways to solve this, i also have asked to ChatGPT but not much success. I'm a student so i don't have any experienced developer to ask for a hint, so i would appreciate any help solving this. Thanks in advance.


Solution

  • The simplest solution would be to exit-out of the remove handler early when the quantity of the associated product is already at 0. The issue currently is that the decrement of totalVolume happens even when the quantity was at 0 when the user pressed the button. (A good enhancement to this app would be to disable the decrement buttons when the quantity is 0)

    To exit early we need add only one line of code:

    removeOneBtn.addEventListener('click', function (event) {
      const index = Number(event.target.dataset.decrease);
      // This is the new line that will exit early.
      if (products[index].quantity === 0) { return; }
    
      // products[index].quantity = Math.max(products[index].quantity - 1, 0);
      // we can remove the Math.max(), since this will never get below 0
      products[index].quantity = products[index].quantity - 1;
      /* rest of remove handler stays the same */
    }
    

    Here is a fiddle for reference.