1

I'm currently learning VueJS for work and I'm trying to build a CRUD app that displays items from an API to a <v-data-table>, and I want to edit, delete, and create new items using <v-dialog>.

This is my main screen:

<template>
  <v-app>
    <v-main>
      <div>
        <v-text-field
          v-model="search"
          append-icon="mdi-magnify"
          single-line
          hide-details
          label="search">
        </v-text-field>
        <v-data-table
          :items="movies"
          :headers="headers"
          :search="search"
        >
          <template v-slot:[`item.actions`]>
            <edit-movie></edit-movie>
            <delete-movie></delete-movie>
            <details-movie></details-movie>
          </template>
        </v-data-table>
      </div>
    </v-main>
  </v-app>
</template>
    <script>
    import {mapState} from 'vuex';
    import DeleteMovie from './components/deleteMovie.vue';
    import DetailsMovie from './components/detailsMovie.vue';
    import EditMovie from './components/editMovie.vue';
    
    export default {
      name: 'App',
      components: {
        EditMovie,
        DeleteMovie,
        DetailsMovie
     
      },
      mounted(){
        this.$store.dispatch('getMovies');
      },
      data: () => ({
        search: ''
      }),
    
      computed: {
        headers() {
          return [
                    {text: "Title", value: "title"},
                    {text: "Overview", value: "overview"},
                    {text: "Votes", value:"vote_average"},
                    {text: 'Actions', value: 'actions', sortable: false },
                    {text: '', value: 'details'},
          ]
        },
            ...mapState({
                movies:state => state.movies
            })
      },  
     }
    
    </script>

and I call the API like this:

export default new Vuex.Store({
  state: {
    movies: [],
  },
  mutations: {
    async getMovies(state){
      let response = await axios.get(`https://api.themoviedb.org/3/movie/now_playing?api_key=${public_key}&language=en-US`)
      .then((result) => {
        result.data.results.forEach(item => {
          console.log(item)
          state.movies.push(item)
        });
      })
      .catch((error) => {
        console.log(error)
      })
    }
  },
  actions: {
    getMovies: context => {
      context.commit('getMovies')
    },
  },
})

Now, my main concern is how to call a single item and display all the details in inside this dialog: (It has to be in a different component)

<template>
  <v-dialog>
    <template  v-slot:activator="{ on, attrs }">
      <v-btn
        small
        class="mr-2"
        v-on="on"
        v-bind="attrs"
        >
        Read More
      </v-btn>
    </template>
    <v-card>
      <v-card-text>
        {{THIS IS WHERE IT SHOULD BE DISPLAYED}}
      </v-card-text>
    </v-card>
  </v-dialog>
</template>
<script>
  export default {
    data: () => ({
  
    }),
  }
</script>

I also don't know how to edit/delete the items from a dialog in a different component.

Anyway, thank you in advance for any help

Dan
  • 59,490
  • 13
  • 101
  • 110
Arianna
  • 43
  • 2
  • 7
  • It would be good to focus the question. Are you asking how to retrieve a single item from the backend or how to modify one of the `movies` items only? – Dan Feb 28 '21 at 18:16
  • I would like to focus on retrieving the single item from the backend for now, like calling the item id with the click of a button in the row of the data table and opening a dialog with the details from the movie – Arianna Feb 28 '21 at 18:26

1 Answers1

0

Here is a way that doesn't use an activator, with a demo. I'll show how to use just an Edit modal, for example, and you can create others by duplicating these steps.

1. Place the modal component outside of the table. Use a button in the slot that sets a v-model value for the modal component when clicked. It will pass the id of the row's item:

Parent (Data Table)

<div>
  <v-data-table :headers="headers" :items="movies">
    <template v-slot:[`item.actions`]="{ item }">
      <v-btn @click.stop="idEdit = item.id">Edit</v-btn>
    </template>
  </v-data-table>

  <edit-movie v-model="idEdit"></edit-movie>
</div>
data: {
  return {
    idEdit: null,
  }
}

2. The modal component uses a computed setter to show the modal if an id value has been passed and emits an event to clear the id when closed:

Child (Modal)

<template>
  <v-dialog v-model="show">
    <v-card>ID: {{ value }}</v-card>
  </v-dialog>
</template>
export default {
  props: ['value'],  // References the `v-model` prop
  computed: {
    // Computed getter / setter
    show: {                 
      get() { return this.value !== null },
      set(value) { this.$emit('input', null) }  // Clear the `v-model` to close
    }
  }
}

3. The modal component can use a watch to load data from the api when the id changes:

watch: {
  value(newValue) {
    if (!newValue) return;
    console.log(`Load data here with ID: ${newValue}`);
  }
}

Here's a demo: https://codepen.io/sh0ber/pen/poNLVvz

The benefit of no activator is creating only one component per action type instead of one per row, per type. It could be further improved to just one total modal component.

Also, use actions for async calls, not mutations.

tony19
  • 125,647
  • 18
  • 229
  • 307
Dan
  • 59,490
  • 13
  • 101
  • 110
  • There are a couple of non-beginner concepts here but hopefully the demo makes everything clear. – Dan Feb 28 '21 at 21:30