2

I am fetching the JSON data(Orders) from REST API and displaying in a dynamic HTML table using Vue js. I have a "Print" button for each row in the table. The purpose of the button is printing the data of the row in a structure, basically a bill. For that, I want to highlight the newly added row until the Print button is clicked by the user. How do I achieve this? I'm refreshing the table every minute. This is my code.

<tr v-for="orders, index in orders">
  <th scope="row">{{ index + 1 }}</th>
    <td>{{ orders.id }}</td>
    <td>{{ orders.billing.first_name + " " +orders.billing.last_name }}</td>
    <td>{{ orders.date_created }}</td>
    <td>{{ orders.billing.phone}}</td>
    <td>{{ orders.billing.address_1 + ", " + orders.billing.address_2 + ", " + orders.billing.city + orders.billing.postcode }}</td>
    <td>{{ orders.line_items.name}}</td>
    <td>{{ orders.total}}</td>
    <td><button class="btn btn-primary" (click)="printBill(data)">Print</button></td>
  </tr>
</tbody>
</table>
</div>
<script>
var app = new Vue({
  el: '#app',
  data: {
    orders: []
  },
  mounted: function() {
    axios.get('https://localhost/Site/wp-json/wc/v3/orders?consumer_key=KEY&consumer_secret=KEY1')
      .then(response => {
        this.orders = response.data;
        console.log(response);
       })
       .catch(error => {
         console.log(error);
        });
     },      
  })
</script>
sebasaenz
  • 1,917
  • 2
  • 20
  • 25
Tom
  • 87
  • 1
  • 9
  • Your data model for each row should include a flag which indicates if it is new or if it has been printed yet, then use that to bind a style to that row. When you print that row, switch off the flag. – Decade Moon Aug 05 '20 at 23:16
  • Thanks for the answer. I am new to web development. May i know what do you mean by flag and bind that once the row is printed? – Tom Aug 05 '20 at 23:26

2 Answers2

1

I wrote a small example, have a look:

<template>
  <div id="app">*
    <tr
      v-for="(order, index) in orders"
      :key="index"
      :class="{highlight: orders[index].isPrinted === undefined}"
    >
      <th scope="row">{{ index + 1 }}</th>
      <td>{{ order.name }}</td>
      <td>{{ order.something}}</td>
      <td>
        <button class="btn btn-primary" @click="printBill(index)">Print</button>
      </td>
    </tr>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      orders: []
    };
  },
  methods: {
    printBill(index) {
      //code to print the bill

      //change flag
      this.$set(this.orders[index], "isPrinted", true);
    }
  },
  mounted() {
    //axios request - data sample
    this.orders = [
      {
        name: "first",
        something: "whatever"
      },
      {
        name: "second",
        something: "whatever"
      },
      {
        name: "third",
        something: "whatever"
      },
      {
        name: "fourth",
        something: "whatever"
      },
      {
        name: "fifth",
        something: "whatever"
      }
    ];
  }
};
</script>

<style>
.highlight {
  background-color: blue;
  color: white;
}
th {
  width: 20%;
}
td {
  width: 20%;
}
</style>

You can run it here.


As you can see that I am adding a flag to elements in orders array whenever printBill method runs.

By tracking newly added property we can conditionally display highlight class.

Jakub A Suplicki
  • 4,586
  • 1
  • 23
  • 31
  • 2
    It is [strongly recommended that you don't use `this.$forceUpdate()`](https://vuejs.org/v2/guide/components-edge-cases.html#Forcing-an-Update). Instead you should change the `isPrinted` flag update to `this.$set(this.orders[index], 'isPrinted', true)` – Daniel Elkington Aug 06 '20 at 00:38
  • 1
    Thanks for your explanation with example. Thank you so much. – Tom Aug 06 '20 at 22:09
1

Add an isPrinted flag to each row of data, making sure you retain this if rows had been previously flagged. Also, call the API every minute.

  mounted: function() {
    // Call the API the first time
    this.refreshData()
    // Then call the API every minute
    this.setIntervalId = setInterval(this.refreshData, 60000)
  },
  beforeDestroy: function() {
    // Stop refreshing data after the component is destroyed!
    clearInterval(this.setIntervalId)
  }
  methods: {
    // Extract refresh logic into a method
    refreshData () {
      axios.get('https://localhost/Site/wp-json/wc/v3/orders?consumer_key=KEY&consumer_secret=KEY1')
      .then(response => {
        // Note the orders we previously flagged as printed, so we can reapply the flag after refreshing
        const previouslyFlaggedIds = this.orders.filter(x => x.is_printed).map(x => x.id);
        this.orders = response.data.map(x => ({...x, is_printed: previouslyFlaggedIds.find(y => y === x.id) != null}));
       })
       .catch(error => {
         console.log(error);
      });
    }
  }

Use this to style the rows

<tr
      v-for="(order, index) in orders"
      :key="order.id"
      :class="{highlight: !order.is_printed}"
    >

Set is_printed when rows are printed.

<td><button class="btn btn-primary" @click="printBill(order)">Print</button></td>
methods: {
    printBill(order) {
      order.is_printed = true
    }
  }
Daniel Elkington
  • 2,560
  • 6
  • 22
  • 35
  • Hi, thanks for your answer. When i added the style code replacing line in my code "" . It throws errors such as "Cannot read property first_name.... Am i inserting the code in a wrong way? – Tom Aug 06 '20 at 01:42
  • @Tom I renamed the variable used for each item from "orders" to order" - can you check that in your template for the the order details you're using `order.id` instead of `orders.id`? – Daniel Elkington Aug 06 '20 at 01:49
  • Hi, thanks for your help, it worked well. Just wanna ask, if I refresh the table to fetch data periodically from API, like every 1 minute through setInterval js function. the flag will set to default again right. so , is there anyway that without refreshing the whole table or page but update the table with new rows if API has updates. Thank you – Tom Aug 06 '20 at 22:08
  • @Tom I've updated my answer to refresh data every minute with an example - this will retain the flag after refreshing. – Daniel Elkington Aug 06 '20 at 22:43
  • 1
    Thank you so much, i could retain the flags without refreshing...Really helped... – Tom Aug 06 '20 at 23:19
  • Hi, sorry to bother, just need a suggestion for my printBill function. I want to print the row data when "Print" button clicked. For that I am thinking to open a modal popup when the print button clicked , the modal need to contain the row data in a bill structure and a print button to print the modal. Is this a good way to do the printing? – Tom Aug 07 '20 at 18:47
  • @Tom Printing is a bit difficult in the web, but we have some control over what gets printed when the user selects File -> Print using `media="print"` css. Your modal approach could work; you'd just need a lot of trial and error to make sure only the right parts of the page get printed. See https://stackoverflow.com/a/6500966/5740181 – Daniel Elkington Aug 08 '20 at 00:19
  • Thanks for the reply. I am actually developed a Woocommerce site for a Pizza shop. So the client wants a easy UI to see the orders and print the bill. Rather than using the Wordpress backend, the custom UI will be easy way. i figured out that there will be automatic order printing plugins in Wordpress to print the order whenever Woocommerce gets an order, this uses Google cloud print. But clients prefer to see the order before he prints the delivery note. What could be the best way to archeive this, in your opinion. – Tom Aug 08 '20 at 00:48
  • @Tom Hmm, not sure - I'd suggest opening a new question. – Daniel Elkington Aug 09 '20 at 11:13
  • Hi, I am just trying to add a sound in my Vue js page. for playing the track I am just using plain js and play() function in the method after refreshData(). But i am unable to get the sound when new entry comes in..... – Tom Sep 13 '20 at 11:54