0

I have a vue component which contains this table, which also has a component inside its only row:

    <template>
        <b-table :items="records">
          <template slot="row-details">
              <child-component/>
          </template>
        </b-table>
    </template>

I'm keeping the data inside the table very simple, for sample purposes:

    data() {
       return {
         records: [{
           name: "Parent Row",
           _showDetails: true
         }]
       };
     }

The child-component inside the row is quite simple too:

<template>
<div>
    <p>{{number}}</p>
</div>
</template>

<script>
export default {
data() {
    return {
      number: Math.random(),
      isUpdating: console.log("Updating Child Component")
    };
  },
}
</script>

If I add a new row in the parent table using records.push(newRow) everything works fine, I can see the new row and, in the child component, number does not change.

BUT If I add the new row using records.unshift(newRow) the child component is reloaded, the "Updating child component" message shows and number changes every time.

Is this the expected behaviour? How can I keep the number so it does not change when I unshift a new record?

I have created a working sample here.

Daniel Marín
  • 1,369
  • 14
  • 36

2 Answers2

2

To minimize re-renders of child components in a b-table, and if your data has a field that is unique for every row (i.e. an ID or primary key), set the primary-key prop to the name of the field that contains the unique row ID. This unique ID will be used as the Vue key for each <tr> element. Vue will then know if it needs to re-render the children or not.

Otherwise, what is happening is that b-table uses the row's index as the key. By pushing new rows on the table, the previous rows' Vue key stays the same (and hence not re-rendered), but if you shift on the top of the rows, those that previously has indexes of 0, 1, 2, etc have been replaced by new rows... and the entire table and it's contents (children) must be re-rendered.

EDIT/UPDATE:

I've discovered the root cause. The row-details slot was always using the row's index as its :key, and not incorporating the primary key value (if available). PR https://github.com/bootstrap-vue/bootstrap-vue/pull/4025 will fix this issue and will be available in the 2.0.0 stable release (being released today hopefully).

UPDATE 2:

BootstrapVue v2.0.0 stable has just been released (2019-09-06)

tony19
  • 125,647
  • 18
  • 229
  • 307
Troy Morehouse
  • 5,320
  • 1
  • 27
  • 38
  • Could you provide some code example? I'm adding the primary-key, but getting the same behavior. I have updated the [working sample](https://codesandbox.io/embed/bootstrap-vue-b-table-sortselect-issue-u2e83) – Daniel Marín Sep 04 '19 at 16:18
  • I'm actually inspecting the `` elements and I can see how the PK is correctly set, so definitely there is something more that is missing here. – Daniel Marín Sep 04 '19 at 16:33
  • The primary key field value in each row **must** be unique... i.e. you are using `name`, but each row is getting the same primary key value (i.e. `'New Row (Append)'`). in order for the `name` field to be usable as a primary key, it must be guaranteed unique across **all** rows in the table (each row has a different `name` value). If you look in the console, you will see that Vue is complaining about non unique key values – Troy Morehouse Sep 04 '19 at 19:20
  • Further explanation: the primary key value must be something (number or string) that can be used by itself to uniquely lookup/find the record (row). Vue uses the key to distinguish a rendered node in the HTML DOM tree to determine what needs to be re-rendered/patched in the DOM. – Troy Morehouse Sep 05 '19 at 03:06
  • I'm afraid that's not the problem. The first time is adding two perfect valid and unique PKs (`First Row` and `New Row (Append)`). The warning does not appear until you click for a second time. Also, in my production environment, I have more elaborated PKs and they are unique. In any case, I have changed [the sample](https://codesandbox.io/embed/bootstrap-vue-b-table-sortselect-issue-u2e83) to get until 10 unique PKs, so you can check is still not working. – Daniel Marín Sep 05 '19 at 08:38
  • 1
    I've discovered the root cause. the details slot was always using the rows index as its `:key`, and not incorporating the primary key value (if available). PR https://github.com/bootstrap-vue/bootstrap-vue/pull/4025 will fix this issue and will be available in the 2.0.0 stable release (being released today hopefully) – Troy Morehouse Sep 05 '19 at 16:13
-1

I think it has something todo with the child component. If you set _showDetails to true to every element you see what is happening. If you press prepend for the first time the number of the prepend will be the current number of parent and parent gets an new number

<template>
  <div>
    <input value="Append Row" @click="appendRow" type="button">
    <input value="Prepend Row" @click="prependRow" type="button">
    <b-table :items="records" :fields="fields">
      <template v-slot:row-details="scope">
        <child-component :number="scope.item.number"/>
      </template>
    </b-table>
  </div>
</template>

<script>
import ChildComponent from "./ChildComponent";

export default {
  data() {
    return {
      fields: ['name'],
      records: [
        {
          name: "Parent Row",
          _showDetails: true,
          number: Math.random()
        }
      ]
    };
  },
  components: {
    "child-component": ChildComponent
  },
  methods: {
    appendRow() {
      let newRow = {
        name: "New Row (Append)",
        _showDetails: true,
        number: Math.random()
      };
      this.records.push(newRow);
    },
    prependRow() {
      let newRow = {
        name: "New Row (Prepend)",
        _showDetails: true,
        number: Math.random()
      };
      this.records.unshift(newRow);
    }
  }
};
</script>

Child-component:

<template>
  <div>
    <p>test {{number}}</p>
  </div>
</template>

<script>
export default {
  props: ['number'],
  data() {
    return {
      isUpdating: console.log("Updating Child Component")
    };
  }
};
</script>
dreijntjens
  • 4,577
  • 26
  • 28