9
votes

I am using Vue, Vuex, and Vuetify to display courses in data-table and want in-line editing as a feature. Please see relevant component code below.

#Courses.vue

<template>
  <v-data-table 
  :headers="headers"
  :items="courses"
  :search="search"
  :loading="loading"
  no-data-text="No Courses Available"
  >

    <template slot="items" slot-scope="props">
      <tr>
        <td>
          <v-edit-dialog
          :return-value.sync="props.item.title"
          lazy
          >
            {{ props.item.title }}
            <v-text-field
            slot="input"
            label="Enter New Course Title"
            v-model="props.item.title"
            single-line
            counter
            @keyup.enter="onUpdateCourse({ id: props.item.id, title: props.item.title})"
            ></v-text-field>
          </v-edit-dialog>
        </td>

        <td>{{ props.item.category }}</td>
        <td>{{ props.item.startDate | date }}</td>
        <td>{{ props.item.endDate | date }}</td>
        <td>{{ props.item.location }}</td>
      </tr>
    </template>
  </v-data-table>
</template>

<script>
  export default {
    data() {
      return {
        headers: [** table headings **],
      };
    },
    computed: {
      courses() {
        return this.$store.getters.courses;
      },
    },
    methods: {
      onUpdateCourse(itemToUpdate) {
        debugger;
        this.$store.dispatch('updateCourse', itemToUpdate);
      },
    },
  };
</script>

It works; but I take issue with the fact that this approach alters the Vuex state directly: the only reason to dispatch(‘updateCourse’, itemToUpdate) is to update the db (firestore in this case). I would have thought it preferable to update the $store.state solely via the Vuex actions/mutations rather than have it syncing directly here.

So, my first question is: Should I take issue with this, and if not why not?

Because it was bugging me, I added a local copy of the Vuex courses array to the computed section:

localCopy() {
  return this.courses.map(x => Object.assign({}, x));
},

and built the data-table using :items="localCopy". This allows me to update the courses from within the Vuex actions but the data table doesn’t update until I click somewhere else in the app.

So, my second question is: If this is the correct approach, how do I keep the reactivity of the component?

Thanks for any help.

(PS – When cutting and pasting code, or editing in the text box, it seems that some of the double quotes " are unfortunately getting converted to fancy quotes . It is doubly unfortunate that I am dyslexic and although I have done my best to hunt them down, I literally can’t see them most of the time. Please don’t hate on me)

1

1 Answers

10
votes

To not assign changes to props.item.title, do:

  • Remove the .sync in <v-edit-dialog :return-value="props.item.title".
  • Replace v-model with :value in <v-text-field :value="props.item.title".

As .sync has an implicit @return-value:update="props.item.title = $event" and v-model has an implicit @input="props.item.title = $event (roughly), the above alone (removing .sync and replacing v-model with :value) would stop title from being directly modified.

To make it being modified via dispatch also add an @input listener that calls the dispatch: @input="onUpdateCourse({ id: props.item.id, title: props.item.title})".

Finally, here's how your code should look like:

<td>
  <v-edit-dialog
  :return-value="props.item.title"
  lazy
  >
    {{ props.item.title }}
    <v-text-field
    slot="input"
    label="Enter New Course Title"
    :value="props.item.title"
    @input="onUpdateCourse({ id: props.item.id, title: props.item.title})"
    single-line
    counter
    @keyup.enter="onUpdateCourse({ id: props.item.id, title: props.item.title})"
    ></v-text-field>
  </v-edit-dialog>
</td>