3
votes

I've built a Vue.js component, which fetches orders from a Woocommerce store. These orders contain the product variations from the products and are received as an object.

In the table, I need to format the object before it is displayed.

My code looks like this:

<template>
<div>

    <vue-good-table
      title=""
      :columns="columns"
      :rows="variationOrders"
      :paginate="true"
      :lineNumbers="true"/>

</div>
</template>

<script>
    export default {
        data: function() {
            return {
                variationOrders: [],
                columns: [
                {
                  label: 'Order#',
                  field: 'order_id',
                  filterable: true,
                },
                {
                  label: 'Customer',
                  field: 'customer_name',
                  //type: 'number',
                  html: false,
                  filterable: true,
                },
                {
                  label: 'QTY',
                  field: 'qty',
                  type: 'number',
                  //inputFormat: 'YYYYMMDD',
                  //outputFormat: 'MMM Do YY',
                },
                {
                  label: 'Product',
                  field: 'product_name',
                  //type: 'percentage',
                  html: false,
                },
                {
                  label: 'Variations',
                  field: this.formatVariations(self.variationOrders),
                  //type: 'percentage',
                  html: true,
                },
                {
                  label: 'Timeslot',
                  field: 'timeslot',
                  //type: 'percentage',
                  html: false,
                },
                {
                  label: 'Transportation',
                  field: 'store_id',
                  //type: 'percentage',
                  html: false,
                },
              ],
            }
        },
        methods: {
            getTotals: function() {
                var self = this;
                var productId = document.getElementById('product-id').getAttribute('data-id');
                axios.get('/api/v1/order_variations/' + productId)
                .then(function (response) {
                    self.variationOrders = response.data.order_variations;
                    //console.log(response.data);
                })
                .catch(function(error) {
                    //
                });
            },
            formatVariations: function(variationOrders) {
              console.log(variationOrders);
            },
        },
        mounted: function() {
            this.getTotals();
            // call the API every 30 seconds to fetch new orders
            setInterval(function () {
                this.getTotals();
            }.bind(this), 5000); 
        }
    }
</script>

On the Variations column I pass a function which is responsible for formatting the string. The function itself works fine, but I am unable to pass the the received object from the API to this function.

I get the following error message in the console:

  1. If I pass this.formatVariations(this.variationOrders) and console.log I get undefined

  2. If I pass this.formatVariations(variationOrders) and console.log I get [Vue warn]: Error in data(): "ReferenceError: variationOrders is not defined"

I have a suspicion that at the time the function is called, that variable doesn't exist yet.

Is there anything I'm missing?

UPDATE 1

I've updated the code to the following. I'm closer but unfortunately the view isn't updating.

MY code looks like this:

<template>
<div>

    <vue-good-table
      title=""
      :columns="formattedColumns"
      :rows="variationOrders"
      :paginate="true"
      :lineNumbers="true"/>

</div>
</template>

<script>
    export default {
        data: function() {
            return {
                variationOrders: [],
                columns: [
                {
                  label: 'Order#',
                  field: 'order_id',
                  filterable: true,
                },
                {
                  label: 'Customer',
                  field: 'customer_name',
                  //type: 'number',
                  html: false,
                  filterable: true,
                },
                {
                  label: 'QTY',
                  field: 'qty',
                  type: 'number',
                  //inputFormat: 'YYYYMMDD',
                  //outputFormat: 'MMM Do YY',
                },
                {
                  label: 'Product',
                  field: 'product_name',
                  //type: 'percentage',
                  html: false,
                },
                {
                  label: 'Variations',
                  field: 'variation',
                  //type: 'percentage',
                  html: true,
                },
                {
                  label: 'Timeslot',
                  field: 'timeslot',
                  //type: 'percentage',
                  html: false,
                },
                {
                  label: 'Transportation',
                  field: 'store_id',
                  //type: 'percentage',
                  html: false,
                },
              ],
            }
        },
        methods: {
            getTotals: function() {
                var self = this;
                var productId = document.getElementById('product-id').getAttribute('data-id');
                axios.get('/api/v1/order_variations/' + productId)
                .then(function (response) {
                    self.variationOrders = response.data.order_variations;
                })
                .catch(function(error) {
                    //
                });
            },
            formatVariations: function(variationOrders) {
              var variationsString = '';
              variationOrders.forEach(function(item) {
                var variations = JSON.parse(item.variation);
                for(var i = 0; i < variations.length; i++) {
                  variationsString = variationsString + variations[i].key + ': ' + variations[i].value + '<br />';
                }
              });
              return variationsString;
            },
        },
        computed: {
          formattedColumns(){
            const formattedVariations = this.formatVariations(this.variationOrders);
            console.log(formattedVariations);
            return this.columns.map(c => {
              if (c.label == "Variations") {
                return {label: "Variations", field: formattedVariations , html: true}
              }
              return c;
            })
          }
        },
        mounted: function() {
            this.getTotals();
            // call the API every 30 seconds to fetch new orders
            setInterval(function () {
                this.getTotals();
            }.bind(this), 5000); 
        },
    }
</script>

Update 2

The formatVariations() function returns a sample set like this:

choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Naked<br />choose-sugar: No<br />choose-your-cake: Naked<br />choose-sugar: No<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Coated<br />choose-sugar: No<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Coated<br />choose-sugar: Yes<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />choose-your-cake: Naked<br />choose-sugar: Yes<br />

Update 3

Here's one array item of what the API returns:

:
customer_name
:
(...)
order_id
:
(...)
product_name
:
(...)
qty
:
(...)
store_id
:
(...)
variation
:
"[{"id": 35, "key": "choose-your-cake", "value": "Naked"}, {"id": 36, "key": "choose-sugar", "value": "Yes"}]"
1

1 Answers

7
votes

self.variationOrders is undefined in the data method; self is only available in the scope of the getTotals method.

Instead, use a computed property to format columns.

computed:{
  formattedColumns(){
    const formattedVariations = this.formatVariations(this.variationOrders)
    return this.columns.map(c => {
      if (c.label === "Variations")
        return {label: "Variations", field: formattedVariations , html: true}

      return c
    })
  }
}

And use the computed property in the template.

<vue-good-table
  title=""
  :columns="formattedColumns"
  :rows="variationOrders"
  :paginate="true"
  :lineNumbers="true"/>

The computed property should be updated whenever variationOrders changes.

Edit

The above answers the question that was asked, but doesn't actually render the desired table (as I understand it). This is because of a misunderstanding in how vue-good-table works.

If I understand correctly, what OP really wants is for the content of the cell in the table to be formatted with HTML. In order to do that, you simply need to use the scoped slot table-row. Here is how the template should look (the columns are abbreviated for this example).

<vue-good-table
  title=""
  :columns="columns"
  :rows="variationOrders"
  :paginate="true"
  :lineNumbers="true">
  <template slot="table-row" scope="props">
    <td>{{ props.row.order_id }}</td>
    <td>{{ props.row.customer_name }}</td>
    <td><span v-html="formatVariations(props.row.variation)"></span></td>
  </template>
</vue-good-table>

I also updated the formatVariations method:

formatVariations: function(variationOrders) {
  let parsed = JSON.parse(variationOrders).map(order => {
    return `${order.key} : ${order.value} <br>`
  })
  return parsed.join('');
},

This is assuming the data format looks like this:

[
  {
    order_id: 1,
    customer_name: "Bob NewHart",
    qty: 10,
    product_name: "Hats",
    variation: '[{"id": 35, "key": "choose-your-cake", "value": "Naked"}, {"id": 36, "key": "choose-sugar", "value": "Yes"}]'
  },
  {
    order_id: 2,
    customer_name: "Mary Lamb",
    qty: 10,
    product_name: "Necklaces",
    variation: '[{"id": 35, "key": "choose-your-cake", "value": "Naked"}, {"id": 36, "key": "choose-sugar", "value": "Yes"}]'
  },
]