Search code examples
javascriptvue.jsvuejs2vuetify.jsvue-cli-4

How to calculate total in javascript to display in datatable column using backend data in Vue.js 2.6, Vue CLI 4, Vuetify 2.2


I'm using Vuetify's current CRUD Datatable UI Component (compatible with Vue.js2) and I'm trying to calculate the subtotal of a product multiplying the product's quantity with its price. Previously I was testing it with static data in javascript, but now I connected the backend to retrieve actual data from the database. This is how I was doing it with static data:

HTML:

<template>
    <v-layout align-start>
        <v-flex>
            <v-container grid-list-sm class="pa-4 white">
                <v-layout row wrap>
                    <v-flex xs12 sm12 md12 lg12 xl12>
                    <v-data-table :headers="headerDetails" :items="details" hide-actions class="elevation-1">
                        <template v-slot:no-data>
                            <h3>There are no current products added on details.</h3>
                        </template>
                    </v-data-table>
                </v-flex>
            </v-layout>
        </v-flex>
    </v-layout>
</template>

JAVASCRIPT:

<script>
    import axios from 'axios'
    export default {
        data(){
            return{

                headerDetails: [
                    { text: 'Product', value: 'product', sortable: false },
                    { text: 'Quantity', value: 'quantity', sortable: false },
                    { text: 'Price', value: 'price', sortable: false },
                    { text: 'Subtotal', value: 'subtotal', sortable: false },
                ],
                details:[
                    {product:'Product 1', quantity:'5', price:'10' },
                    {product:'Product 2', quantity:'5', price:'20' },
                    {product:'Product 3', quantity:'20', price:'15' },
                    {product:'Product 4', quantity:'10', price:'25' }
                ].map(detail => ({...detail, subtotal: detail.quantity*detail.price}) ),
            }
        }
    }
</script>

Adding an Array.map .map(detail => ({...detail, subtotal: detail.quantity*detail.price}) ), did the trick to calculate the total for each product, but only when displaying the static data as you can see in the following example:

|---------------------|------------------|---------------------|------------------|
|       Product       |     Quantity     |        Price        |     Subtotal     |
|---------------------|------------------|---------------------|------------------|
|      Product 1      |         5        |         10          |        50        |
|---------------------|------------------|---------------------|------------------|
|      Product 2      |         5        |         20          |       100        |
|---------------------|------------------|---------------------|------------------|
|      Product 3      |        20        |         15          |       300        |
|---------------------|------------------|---------------------|------------------|
|      Product 4      |        10        |         25          |       250        |
|---------------------|------------------|---------------------|------------------|

Now that I've connected the backend, I had to erase the static data since it's not useful anymore, so I've changed the code a bit. Here is how it looks now:

UPDATED HTML:

<template>
    <v-layout align-start>
        <v-flex>
            <v-container grid-list-sm class="pa-4 white">
                <v-layout row wrap>
                    <v-flex xs12 sm8 md8 lg8 xl8>
                        <v-text-field @keyup.enter="searchBarcode()" v-model="code" label="Barcode">
                        </v-text-field>
                    </v-flex>
                    <v-flex xs12 sm12 md12 lg12 xl12>
                        <v-data-table :headers="headerDetails" :items="details" hide-default-footer class="elevation-1">
                            <template v-slot:no-data>
                                <h3>There are no current products added on details.</h3>
                            </template>
                        </v-data-table>
                    </v-flex>
                </v-layout>
            </v-container>
        </v-flex>
    </v-layout>
</template>

UPDATED JAVASCRIPT:

<script>
    import axios from 'axios'
    export default {
        data(){
            return{
                headerDetails: [
                    { text: 'Product', value: 'product', sortable: false },
                    { text: 'Quantity', value: 'quantity', sortable: false },
                    { text: 'Price', value: 'price', sortable: false },
                    { text: 'Subtotal', value: 'subtotal', sortable: false },
                ],
                details:[].map(detail => ({...detail, subtotal: detail.quantity*detail.price}) ),
                code: '',
            }
        }, 
        methods: {
            searchBarcode(){
                let me=this;
                axios.get("api/Products/SearchProductBarcode/" + this.code).then(function(response){
                    me.addDetail(response.data);
                }).catch(function(error){
                    console.log(error);
                }); 
            }
            addDetail(data = []){
            this.details.push(
                {idproduct: data['idproduct'],
                product: data['name'],
                quantity: 10,
                price: 150}
            );
        },
        }
    }
</script>

What I'm attempting is to input the existing barcode in the text field and call the searchBarcode() which is triggered with a keyup.enter event. If the barcode exists in the database, it retrieves its data (Product Name, Quantity, Price) and adds it to the datatable as you can see in the following picture:

enter image description here

As you can see, the data is displayed as it should, except the subtotal. How can I calculate the total by multiplying the product's price with its quantity if there's nothing inside the details:[] array unless it's filled after triggering the event?


Solution

  • Computed properties to the rescue

    <template>
      <v-layout align-start>
        <v-flex>
          <v-container grid-list-sm class="pa-4 white">
            <v-layout row wrap>
              <v-flex xs12 sm8 md8 lg8 xl8>
                <v-text-field
                  v-model="code"
                  label="Barcode"
                  @keyup.enter="searchBarcode()"
                >
                </v-text-field>
              </v-flex>
              <v-flex xs12 sm12 md12 lg12 xl12>
                <v-data-table
                  :headers="headerDetails"
                  :items="detailsWithSubTotal"
                  hide-default-footer
                  class="elevation-1"
                >
                  <template v-slot:no-data>
                    <h3>There are no current products added on details.</h3>
                  </template>
                </v-data-table>
              </v-flex>
            </v-layout>
          </v-container>
        </v-flex>
      </v-layout>
    </template>
    
    <script>
    import axios from 'axios'
    export default {
      data() {
        return {
          headerDetails: [
            { text: 'Product', value: 'product', sortable: false },
            { text: 'Quantity', value: 'quantity', sortable: false },
            { text: 'Price', value: 'price', sortable: false },
            { text: 'Subtotal', value: 'subtotal', sortable: false }
          ],
          details: [],
          code: ''
        }
      },
      computed: {
        detailsWithSubTotal() {
          // Each new added detail, updates the detailsWithSubTotal
          // computed property, so you have the subtotal calc in
          // each detail
          return this.details.map((detail) => ({
            ...detail,
            subtotal: detail.quantity * detail.price
          }))
        }
      },
      methods: {
        searchBarcode() {
          axios
            .get('api/Products/SearchProductBarcode/' + this.code)
            .then(function(response) {
              this.addDetail(response.data)
            })
            .catch(function(error) {
              console.log(error)
            })
        },
        addDetail(data = []) {
          this.details.push({
            idproduct: data['idproduct'],
            product: data['name'],
            quantity: 10,
            price: 150
          })
        }
      }
    }
    </script>