vue.jsdomvue-componentvuetify.js

v-data-table-server not updating component in the table template


I have a Vuetify v-data-table-server (docs) which updates itself by fetching data from API.

<template>
  <v-data-table-server
    v-if="events.length > 0"
    v-model:items-per-page="events_limit"
    :headers="headers"
    :items-length="events_total"
    :items="events"
    :loading="loading"
    item-key="e_id"
    @update:options="loadItems">

      <template #item.e_created_at="{ item }">
        <event-date :initialDate="item.raw.e_created_at" />
        {{ item.raw.e_created_at }}
      </template>
      
      <template #item.e_status="{ item }">
        {{ item.raw.e_status }}
      </template>
  </v-data-table-server>
</template>

<script>
import userService from "user.service";
import EventDate from "EventDate.vue";

export default {
  name: "Events",
  props: ["page"],
  computed: {},
  components: {
    EventDate,
  },
  data: () => ({
    isLoading: false,
    isSuccess: false,
    events: [],
    events_offset: 0,
    events_limit: 10,
    loading: true,
    headers: [
      {
        title: "Date",
        align: "start",
        sortable: true,
        key: "e_created_at",
      },
      {
        title: "Result",
        align: "start",
        sortable: true,
        key: "e_status",
      },
    ],
  }),
  mounted() {
    this.loadItems({ page: 1, itemsPerPage: 10 });
  },
  methods: {
    loadItems({ page, itemsPerPage, sortBy }) {
      this.loading = true;
      userService
        .get_events({
          limit: itemsPerPage,
          offset: (page - 1) * itemsPerPage,
          sort_by: sortBy,
        })
        .then(
          (response) => {
            this.events_total = response.events_total;
            this.events = response.events;
            this.loading = false;
          },
          (error) => {
            console.log(error);
          }
        )
    },
  },
};
</script>

I also have an EventDate component which is simply formatting the date:

<template>
  <div>
    <span>{{ formattedDate }}</span>
  </div>
</template>

<script>
export default {
  name: "EventDate",
  props: {
    initialDate: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      date: "",
    };
  },
  computed: {
    formattedDate() {
      const dt = new Date(this.date);
      const day = dt.getDate().toString().padStart(2, "0");
      const month = (dt.getMonth() + 1).toString().padStart(2, "0");
      const year = dt.getFullYear();
      const hours = dt.getHours().toString().padStart(2, "0");
      const minutes = dt.getMinutes().toString().padStart(2, "0");
      const seconds = dt.getSeconds().toString().padStart(2, "0");

      return `${day}.${month}.${year} ${hours}:${minutes}:${seconds}`;
    },
  },
  created() {
    this.date = this.initialDate; // Set the initial date when component is created
  },
};
</script>

When the first page is fetched, everything seems normal: components are showing formatted data.

enter image description here

But when I jump to another page, though I get correct values from API and I even see them in the table, EventDate component instances are not updated:

enter image description here

What am I missing?


Solution

  • OK, ChatGPT got it right.

    When the table updates its data from the API, Vue tries to be as efficient as possible in re-rendering the components. In some cases, components with similar properties are re-used rather than destroyed and recreated. As a result, the created() lifecycle hook might not be called again when you change the page.

    I added this to my EventDate.vue:

    export default {
      ...,
      watch: {
        initialDate(newVal) {
          this.date = newVal;
        },
      },
      ...
    }
    

    And now both parts of the cell are updated when I navigate through table pages.