chataolauj
chataolauj

Reputation: 119

Why are my dialogs stacked on top of one another? (Vuetify)

So, I'm learning Vuetify and am implementing cards and dialogs. I'm just following the examples in the Vuetify documentation. What I'm trying to accomplish with my implementation:

Problem #1: It seems the dialog for all the items in the list opens at the same time, which causes them to stack on top of each other.

Problem #2: The wrong the dialog box opens for the item that is being clicked on. I'm assuming this will be fixed if Problem #1 is fixed.

Problem #3: When I click on the "Add to Cart" button, it does the loading and also opens up the dialog for that item.

Question #1: How do I make it so that only one dialog is opened and that it is the correct dialog?

Question #2: How do I make it so that the dialog doesn't open when I click on "Add to Cart"?

I'm opening dialogs without activator; similar to the first example in the Vuetify documentation. I've also tried dialog with activator, but it still had the same issues.

CODEPEN: https://codepen.io/chataolauj/pen/RwwYxBg?editors=1010

Here is the HTML:

<v-app id="app">
  <v-container>
    <v-row>
      <v-col cols="4" height="100%" v-for="(food, index) in foods" :item="food" :key="index">
        <v-card class="mx-auto" :loading="loading && current_index === index" @click.stop="dialog = true">
          <v-img height="200px" :src="food.imgURL"></v-img>
          <v-card-title class="mb-2 pb-0">{{food.name}}</v-card-title>
          <v-card-subtitle>{{food.type}}</v-card-subtitle>
          <v-card-actions>
            <v-btn color="green" text @click="addToCart(index)">Add to Cart</v-btn>
          </v-card-actions>
        </v-card>
        <v-dialog v-model="dialog" max-width="50%">
          <v-card>
            <v-row no-gutters class="pa-4">
              <v-col cols="6" max-width="25%">
                <v-img :src="food.imgURL" class="pa-0" height=175></v-img>
              </v-col>
              <v-col cols="6" max-width="25%" class="pl-2 d-flex flex-column">
                <v-card-title class="pa-0">{{food.name}}</v-card-title>
                <v-subtitle>{{food.type}}</v-subtitle>
                <v-text>{{food.description}}</v-text>
              </v-col>
            </v-row>
          </v-card>
        </v-dialog>
      </v-col>
    </v-row>
  <v-container>
</v-app>

Here is the JavaScript:

new Vue({
  el: "#app",
  vuetify: new Vuetify(),
  data() {
    return {
      loading: false,
      current_index: null,
      dialog: false,
      foods: [
        {
          type: 'Fruit', 
          name: 'Apple',
          imgURL: 'https://placebeard.it/400x400',
          description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
        },
        {
          type: 'Fast Food', 
          name: 'Pizza',
          imgURL: 'https://placebeard.it/400x400',
          description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sit amet luctus venenatis lectus magna fringilla. Sem viverra aliquet eget sit amet tellus. Porta lorem mollis aliquam ut porttitor leo. Augue eget arcu dictum varius duis at consectetur.'
        },
        {
          type: 'Meat',
          name: 'Steak',
          imgURL: 'https://placebeard.it/400x400',
          description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed egestas egestas fringilla phasellus faucibus scelerisque. Diam maecenas sed enim ut sem viverra aliquet eget. Sagittis nisl rhoncus mattis rhoncus urna neque viverra. Quam id leo in vitae turpis massa.'
        }
      ]
    }
  },
  methods: {
    addToCart(index) {
      this.loading = true;
      this.current_index = index;

      setTimeout(() => (this.loading = false), 2000);
    }
  }
});

Upvotes: 3

Views: 2250

Answers (2)

chataolauj
chataolauj

Reputation: 119

So, what I did to solve Problem #1 was by creating an object and populating the object with properties based on the number of items in my array.

data() {
    return {
      loading: false,
      current_index: null,
      previousItem: null,
      dialog: {}, //object to be populated with properties
      foods: [ /* some objects*/ ]
    }
}

I added properties to the object within the created() lifecycle hook.

created() {
  for(let i = 0; i < this.foods.length; i++) {
    this.dialog['dialog_' + i] = false;
  }
}

These different properties that are being added to the dialog object will be the v-model for the cards based on their index. For example, the first card's v-model would be dialog_0. This way only the dialog for the card that was clicked on will open and not the dialog for every card.

<v-col v-for="(food, index) in foods" :item="food" :key="index">
   <v-dialog v-model="dialog['dialog_' + index]"> //v-model
      <template v-slot:activator="{ on: {click} }"> //clicking the card is the activator for the dialog
         <v-card> /*stuff goes here */ </v-card>
      </template>
   </v-dialog>
</v-col>

As for how I solved Problem #3, I just followed DjSh's answer of adding .stop to @click, so it would be @click.stop="addToCart(index)"

Upvotes: 1

DjSh
DjSh

Reputation: 2764

The reason that all the dialogs are opened is that you are using the same v-model for all of them, so every time you trigger one, others get triggered. For this reason, I have added a dialog property to every food object in foods array:

foods: [
    {
      type: 'Fruit', 
      name: 'Apple',
      imgURL: 'https://placebeard.it/400x400',
      description: "....",
      dialog: false
    },,]

Further more, you can easily achieve what you want by using v-slot:activator="{ on }" which according to Vueitfy Docs

designate a custom activator when the activator slot is not used. String can be any valid querySelector and Object can be any valid Node.

by changing @click="addToCart(index) to @click.stop="addToCart(index)" you can stop the dialog from loading.

The template to use is as follow:

 <v-dialog
    v-model="dialog">
    <template v-slot:activator="{ on }">
      // what activates the dialog goes here
      <v-btn
        color="red lighten-2"
        dark
        v-on="on"
      >
        Click Me to open dialog
      </v-btn>
    </template>

    <v-card>
      //content of dialog
    </v-card>
</v-dialog>

I have created a Codepen.

I hope it helps :)

Upvotes: 0

Related Questions