Can't access data in getter when binding by v-if but can access when rendered by v-for in Vue

1k views Asked by At

I have a table that renders based on computed properties like this. Notice how each row gets its style based on its contents. This works dandy.

computed: { rows: function () { return this.$store.getters.tableRows; }, ... }

<tr v-for="row in rows" v-bind:class="rowClass(row)">
  <td v-for="(item, key, index) in row.columns" v-bind:class="classy(index)">{{item}}</td>
</tr>

I also wanted to add a collective info outside of the table, so I set up the following messaging conditional.

computed: {
  finished: function () {
    return this.rows.length === 5;
  },
  whoopsie: function () {
    return this.rows[this.rows.length - 1].columns.elapsed < 0;
  }, ...
}

<div v-if="finished" class="alert alert-success">Done.</div>
<div v-if="whoopsie" class="alert alert-warning">Oops.</div>

The first one works just as expected. However, the second one throws me an error that cannot read property 'columns' of undefined, which looks insane as I can clearly see the data on the screen and the length property is definitely not zero.

I tried to use debugger command to poke around there but it seems not to be invoked at that part of the code. I know the code gets executed because changing return false to return true does render a difference. And debugger seems to work at other places in the code. Weird...

I tried to store the value to a global variable as shown below. Poking in that variable from console window produces precisely the values I was expecting. I can't understand what's going on there but it's definitely some black magic.

whoopsie: function () {
  window.wtf = this.rows[this.rows.length - 1];
  // return true;
  return false;
}, ...
  1. What can I do to get my hands on the values in the computed property?
  2. Why doesn't it do what a sane person would expect it to?
  3. How can debugger be invoked in there?

Edit

The store has the following getter.

tableRows: function(state) { return state.dataTable.rows; }

It's being assigned to the following object. Each row in the data has an ID and a container columns.

const rows = [];
for (let i = 0; data && i < data.length; i++)
rows.push({
  id: data[i].id,
  columns: {
    ...
    elapsed: recalculate(data[i].span)
  }
});

I want to bind to two computed properties that will be true if the number of rows in the table is 5 (that's finished) and if the value of elapsed on the last of the rows is negative (that's woopsie). That why I'm declaring those two (and only the first one's working).

1

There are 1 answers

1
Saurabh On

Error seems in this particular code to me, You can not have (item, key, index) with v-for with an array of objects, that syntax is only available with object, see the fiddle.

so You need something like following:

<tr v-for="row in rows" v-bind:class="rowClass(row)">
  <td v-for="(item, index) in row.columns" v-bind:class="classy(index)">{{item}}</td>
</tr>