Using Vue.js for real-time price calculations

Currently, I'm in the process of creating a hotel booking application and focusing on the booking feature.

Within this step, users are able to modify their preferred room type, number of adults, and number of children using a selection box.

To clarify, if a user selects a "standard room" with one adult and no children, the default price value for that specific room will be retrieved.

The fiddle link up to this point is provided here:

https://jsfiddle.net/c1ud4mkv/.

I require assistance moving forward from this stage. If a user wishes to stay with 2 adults and one or two children, the pricing needs to adjust accordingly.

For example; the current price for a standard room with 1 adult is Rs.1000.

If an extra adult is added by the user, the price should change to Rs.2000. And if a child is added along with 2 adults, the total price should be calculated as Rs.3000 (Rs.2000 + Rs.1000) (Rs.1000 is the price for each additional child).

The HTML code snippet:

<div id="app">
 <table>
    <thead>
     <td>Room Type</td>
     <td>Adult</td>
     <td>Children</td>
     <td>Total Price</td>
    </thead>
        <tbody>
          <tr v-for="(row, index) in rows">
            <td>
              <select data-value="Room Type">
                <option v-for="room in rooms">{{room.title}}</option>
              </select>
            </td>
            <td>
              <select data-value="1 Adult">
              <option value="1">1 Adult</option>
              <option value="2">2 Adults</option>
            </select>
          </td>
          <td>
            <select data-value="No Child">
            <option value="1">No Child</option>
            <option value="2">1 Child</option>
            <option value="3">2 Children</option>
          </select>
        </td>
        <td v-for="item in items">
          {{item.price}}
        </td>
      </tr>
    </tbody>
  </table>
    <div class="add-remove">
    <div class="col-md-6 text-center add">
      <a @click="addRoom" class="add-room"> Add Room </a>
    </div>
    <div class="col-md-6 text-center remove">
      <a @click="removeRoom(index)" class="remove-room"> Remove Room </a>
    </div>
  </div>
</div>

The script being used was:

new Vue({
  el: '#app',

  data: {
    rows: [],
    rooms: [
    {
        value:0,
        title: 'Standard Room'
    },
    {
        value:0,
        title: 'Deluxe Room'
    },
    ],
    items: [{
        price: 1000,
    }]
  },

  methods: {
    addRoom: function() {
      var elem = document.createElement('tr');
      this.rows.push({
      });
    },

    removeRoom: function(index) {
      this.rows.splice(index, 1);
    },

    }

  })

For the corresponding JSFiddle visit this link.

Answer №1

The total price should be dynamically computed based on the provided data.

Update

If you require multiple rows, each with similar behavior, you'll need a component representing a row, named reservation in this case. You'll notice that this component is essentially a replica of the previous Vue component with minimal changes.

The top-level vue template now acts as a loop for generating these components. It's important to mention that a component can reference shared data (rooms) directly from the store but must obtain uniqueness through its properties.

It's worth noting that the entire page state is managed by a well-structured store, separate from Vue components, ensuring no dependencies between different state items. The calculated value, totalPrice, is not stored directly but rather derived from existing values. This problem-solving approach involves setting up the store first before designing the display logic.

AddRow is defined as a method because it triggers at specific times, updates the store, and does not return any value. Methods usually handle pushing data upstream. On the other hand, TotalPrice is declared as a computed property to ensure reactivity, being downstream and used within templates.

You can see the working example here.

Here is the complete implementation...

Markup

<div id="vueRoot">
  <reservation v-for="myReservation in store.reservations" :reservation="myReservation"></reservation>
  <a class="myPlus" v-on:click="addRow">+</a>
</div>

Code

var vueStore = {
  rooms : [
    {label : 'palatial suite', price : 1000.73},
    {label : 'presidential suite', price : 2000.36}
  ],
  reservations : [{
      selectedRoom : null,
      numAdults : 1,
      numChildren : 0
  }]
};
Vue.component("reservation",{
  template : `<div style="padding:12px">
  Room :
  <select v-model="reservation.selectedRoom">
    <option v-for="room in rooms" v-bind:value="room.price">
      {{room.label}}
    </option>
  </select>
  Number of adults :
  <select v-model="reservation.numAdults">
    <option>1</option>
    <option>2</option>
  </select>
  Number of children :
  <select v-model="reservation.numChildren">
    <option>0</option>
    <option>1</option>
    <option>2</option>
  </select>
  Total Price : {{totalPrice}}
</div>`,
  data : function(){
    return {
      rooms : vueStore.rooms
    }
  },
  props : [ 'reservation' ],
  computed : {
    totalPrice : function(){
      if(this.reservation.selectedRoom){
        return this.reservation.selectedRoom 
          + this.reservation.numAdults * 500 
          + this.reservation.numChildren * 200
      }
      else return '';
    }
  }
});
vm = new Vue({
  el : "#vueRoot",
  data : {store : vueStore},
  methods : {
  addRow : function(){
  this.store.reservations.push({
      selectedRoom : null,
      numAdults : 1,
      numChildren : 0
  })
  }
  }
});

Answer №2

To tackle this issue effectively, consider implementing a component. However, based on the current configuration provided, I have made some modifications to generate a functional solution:

Beginning with the HTML portion, take note of how I utilized v-model to correctly link the select elements to your Vue instance.

<div id="app">
<table>
<thead>
<td>Room Type</td>
<td>Adult</td>
<td>Children</td>
<td>Total Price</td>
</thead>
        <tbody>
          <tr v-for="(row, index) in rows">
            <td>
              <select v-model="row.roomType">
                <option v-for="room in rooms" v-bind:value="room.title">{{room.title}}</option>
              </select>
            </td>
            <td>
              <select v-model="row.adultCount">
              <option value="1">1 Adult</option>
              <option value="2">2 Adults</option>
            </select>
          </td>
          <td>
            <select v-model="row.childCount">
            <option value="0">No Child</option>
            <option value="1">1 Child</option>
            <option value="2">2 Children</option>
          </select>
        </td>
        <td>
          {{calcRoomTotal(row)}}
        </td>
      </tr>
    </tbody>
  </table>
    <div class="add-remove">
    <div class="col-md-6 text-center add">
      <a @click="addRoom" class="add-room"> Add Room </a>
    </div>
    <div class="col-md-6 text-center remove">
      <a @click="removeRoom(index)" class="remove-room"> Remove Room </a>
    </div>
  </div>

Moving on to the JavaScript segment, observe how I updated the "Add row" function to incorporate the room properties.

new Vue({
  el: '#app',

  data: {
    rows: [{
      roomType : "Standard Room",
      adultCount : 1,
      childCount : 0
      }],
    rooms: [
    {
        value:0,
        title: 'Standard Room'
    },
    {
        value:0,
        title: 'Deluxe Room'
    },
    ],
    items: [{
        price: 1000,
    }]
  },

  methods: {
    addRoom: function() {
      this.rows.push({
      roomType : "Standard Room",
      adultCount : 1,
      childCount : 0
      });
    },

    removeRoom: function(index) {
      this.rows.splice(index, 1);
    },

    calcRoomTotal: function(row) {
        return (parseInt(row.adultCount) + parseInt(row.childCount)) * 1000;
    }   
}

Lastly, note that the newly added calcRoomTotal method is employed to showcase the total cost for each room line.

For convenience, here's a JSFiddle link with the revised configuration.

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

Refresh the pagination in a jQuery DataTable

I have incorporated DataTable for pagination within my table. The data in the table is loaded through ajax requests, and I am utilizing my custom functions to populate the table manually by interacting with its DOM elements. However, I am facing an issue ...

Currently, I am developing a Vue.js chat application. I am facing a challenge when it comes to sending the entire input div along with the keyboard popping up on Android devices. How can I achieve this

I'm in the process of developing a chat application using vuejs that will be accessed through a webview on Android. I currently have an input-bx element for sending messages, but I need to find a way to ensure it remains visible when the Android keybo ...

JavaScript: Leveraging arrays as key values

Is there a way in javascript to create an Object with keys as numbers and values as arrays of objects? I am aiming to generate an object structured like this: {Key: "1", value: [Object, Object, ...], key: "2", value: [Object, Object, ...], key:"N", value ...

Create a word filter that doesn't conceal the words

I have a code snippet that filters a JSON object based on name and title. I also have an array of specific words and I would like to modify the filter to highlight those words in the text without hiding them. $scope.arrayFilter=["bad,bill,mikle,awesome,mo ...

Changes to the className of a React component will trigger a re-render of

When the className of the parent changes, React children will re-render. import React from 'react'; import { useSelector } from 'react-redux'; import items from './ItemsList.js'; import Item from './Item'; import &ap ...

Error: A TypeError occurred with the startup because it was unable to read the property 'Collection' as it was

Recently, I encountered a series of problems one after another. The first issue was: TypeError [CLIENT_MISSING_INTENTS]: Valid intents must be provided for the Client To resolve this problem, I made changes to my code from: const Discord = require(" ...

What could be causing my image not to show up on ReactJS?

I'm new to ReactJS and I am trying to display a simple image on my practice web app, but it's not showing up. I thought my code was correct, but apparently not. Below is the content of my index.html file: <!DOCTYPE html> <html> & ...

Angular form encountering problem with receiving JSON data from Express route

Here is my angular controller for handling user sign-up/sign-in: angular.module('SignCtrl', []).controller('SignController', function($scope) { $scope.formData = {}; $scope.processForm = function() { $scope.submitted ...

What is the process for activating my redux function within a component?

I'm working on a form that should create a new user when submitted. In my handleCreate function, I want to use Redux to trigger the addUser action and update the state to add the new user. However, it seems like I'm having trouble calling the act ...

What is causing my website to refresh before I can save the edits I have made after clicking on the edit button

My website is fairly simple, allowing users to add blog posts with a title, author, and content. Each post has two buttons - one for deleting and one for editing. These actions add, delete, or edit the chosen posts within a .json file through a local json ...

Whenever I work with NPM, node.js, and discord.js, I consistently encounter the error message stating "TypeError: Cannot read property 'setPresence' of null."

I'm developing a Discord bot with NPM and discord.js, but I keep encountering an error that says "TypeError: Cannot read property 'setPresence' of null". Here is my bot code: const Discord = require('discord.js'); const { prefix, ...

Extracting repeated information from a form_for select box using jQuery looped through

How can I duplicate the output of a Rails loop and display it in a different section of the webpage? This is the ERB code I am currently using: <%= form_for @order do |f|%> <div class="col-xs-6"> <%= f.fields_for :units, @orde ...

Puppeteer: effective method for identifying and interacting with frequent popups in a loop

Seeking assistance in handling multiple popup windows simultaneously. Referencing the code snippet below: const newPagePromise = new Promise(res => browser.on('targetcreated', target => res(target.page()))); for(const dataWorkSheet ...

What is the best way to avoid having multiple files in a JavaScript file input when a user selects a new file?

I am trying to implement a file input using vanilla JavaScript, and my goal is to restrict the user to uploading only a single file at a time. The issue I am facing is that if the user repeatedly selects a file and clicks the upload button, the file gets ...

Assistance needed to make a jQuery carousel automatically rotate infinitely. Having trouble making the carousel loop continuously instead of rewinding

Currently, I am in the process of creating an auto-rotating image carousel using jQuery. My goal is to make the images rotate infinitely instead of rewinding back to the first image once the last one is reached. As a beginner in the world of jQuery, I&apos ...

Using Mongoose to calculate and retrieve the total sum of elements within an array

In the following schema, I have defined the structure: let postScheme = new Schema({ title: { type: String, required: true }, body: String, isImage: Boolean, imageUrl: String, icon: String, time: { type: ...

Hiding Bootstrap Popover When User Clicks Outside of it

My webpage has dynamically loaded content featuring popovers which need to be bound to the body for correct loading and appearance. I am looking for a solution to hide the popovers when a user clicks outside them or on another popover trigger. After some ...

How can you resize a circle in Three.js without resizing its outline?

I'm currently using a THREE.Path to generate a Circular path and then utilizing a TubeGeometry to form a circle with transparent fill and an adjustable stroke thickness. My main query revolves around the process of scaling up the Circular path dynamic ...

following the history.back() function call, the subsequent codes are executed

<?php $ok_register = 0; if($ok_register != 1) { ?> <javascript type="text/javascript"> alert("1"); history.back(); </javascript> <?php } ?> <javascript type="text/javas ...

Having trouble with the form parsing not functioning properly

I have been utilizing Express.js along with the body-parser module for form parsing on the server. However, I am facing an issue where the content received appears as an empty object under res.body. This is how my app.js looks: var express = require("exp ...