When implementing v-model on a v-dialog component, it causes the element within an array to

There is an array of apps with links, and a Dialog component that has v-model="dialog" to close it. However, after adding v-model to the v-dialog, the functionality of the apps is affected. The current app is passed as a prop to Dialog, but it always sends the last app in the apps array. I'm unsure where I've gone wrong.

Parent component

<v-col  v-for="app in apps" :key="app._id" :cols="setBreakpointCols">
      <v-card class="pa-2 " outlined height="230px">
        <v-dialog v-model="dialog">
          <template v-slot:activator="{ on: dialog, attrs }">
            <v-tooltip right color="#363636" max-width="250px">
              <template v-slot:activator="{ on: tooltip }">
                <v-img :src="getImgUrl(app.src)" class="white--text align-end"
                  gradient="to bottom, rgba(0,0,0,.5), rgba(0,0,0,.5)" height="85%" v-bind="attrs"
                  v-on="{ ...tooltip, ...dialog }">
                  <v-card-title v-text="app.title"></v-card-title>
                </v-img>
              </template>
              <span v-if="app.vpnRequired">VPN needed to run!</span>
              <v-divider class="my-1" v-if="app.vpnRequired" dark ></v-divider>
              <span>{{ app.description }}</span>
            </v-tooltip>
            <h1>{{app.link}}</h1>
          </template>
          <h1>{{app.link}}</h1>
          <Dialog @closeDialog="closeDialog" :app="app" />
        </v-dialog>
        <v-card-actions mb-2>
          <v-spacer></v-spacer>
          <v-tooltip top color="#363636">
            <template v-slot:activator="{ on, attrs }" v-if="app.quickActions">
              <v-btn @click="DeleteAppFromQuickActions(app)" icon>
                <v-icon v-bind="attrs" v-on="on" >
                  mdi-minus-box-outline
                </v-icon>
              </v-btn>
            </template>
            <template v-slot:activator="{ on, attrs }" v-else>
              <v-btn @click="AddAppToQuickActions(app)" icon>
                <v-icon v-bind="attrs" v-on="on">
                  mdi-plus-box-outline
                </v-icon>
              </v-btn>
            </template>
            <span v-if="app.quickActions">Delete from QuickActions</span>
            <span v-else>Add to QuickActions</span>
          </v-tooltip>
          <v-spacer></v-spacer>
        </v-card-actions>
      </v-card>
    </v-col>

Child component

<template>
  <v-container fluid>
    <v-row>
      <v-col fixed style="background:#363636;" class="d-flex justify-end" cols="12">
        <v-btn plain color="grey" :href="app.link" target="_blank">
          <v-icon>mdi-open-in-new</v-icon>
        </v-btn>
        <v-btn @click="closeDialog" plain color="grey">
          <v-icon>mdi-close-box-outline</v-icon>
        </v-btn>
      </v-col>
      <v-col cols="12">
        <iframe :src="app.link" width="100%" height="800px"
          frameborder="0">
        </iframe>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>

export default {
  name: "Dialog",
  props: ['app'],
  data() {
    return {
    }
  },
  methods: {
    closeDialog: function (){
      this.$emit('closeDialog')
    }
  }


}
</script>

Answer №1

A modal component doesn't need to be created for each item in a for-loop.

Creating modals within a for-loop results in separate instances of modal components being created, all of which are bound to the same v-model. Consequently, only the last item's data will be passed because the last v-model will always be active.

This issue can be resolved by either passing the array index or creating a separate v-model using an object. However, it is best practice to place the modal component outside the for loop and use it appropriately by passing the data of each selected item as a prop.

So instead of creating modals in a for-loop, you can structure your code like this (dummy data used for UI creation):

PARENT COMPONENT -

<template>
    <div>
        <!-- To open dialog on click on any item -->
        <Dialog
            v-if="dialog && selectedApp"
            v-model="dialog"
            :app="selectedApp"
            @closeDialog="closeDialog"
        />

        <!-- Loop on items -->
        <v-col v-for="app in apps" :key="app._id" cols="12">
            <v-card class="pa-2 " outlined height="230px">
                <v-dialog v-model="dialog">
                    <template v-slot:activator="{ on: dialog, attrs }">
                        <v-tooltip right color="#363636" max-width="250px">
                            <template v-slot:activator="{ on: tooltip }">
                                <v-img
                                    :src="app.src"
                                    class="white--text align-end"
                                    gradient="to bottom, rgba(0,0,0,.5), rgba(0,0,0,.5)"
                                    height="85%"
                                    v-bind="attrs"
                                    v-on="{ ...tooltip, ...dialog }"
                                    @click="selectedApp = app"
                                >
                                    <v-card-title v-text="app.title"></v-card-title>
                                </v-img>
                            </template>
                            <span v-if="app.vpnRequired">VPN needed to run!</span>
                            <v-divider class="my-1" v-if="app.vpnRequired" dark></v-divider>
                            <span>{{ app.description }}</span>
                        </v-tooltip>
                        <h1>{{ app.link }}</h1>
                    </template>
                    <h1>{{ app.link }}</h1>
                </v-dialog>
                <v-card-actions mb-2>
                    <v-spacer></v-spacer>
                    <v-tooltip top color="#363636">
                        <template v-slot:activator="{ on, attrs }" v-if="app.quickActions">
                            <v-btn @click="DeleteAppFromQuickActions(app)" icon>
                                <v-icon v-bind="attrs" v-on="on">
                                    mdi-minus-box-outline
                                </v-icon>
                            </v-btn>
                        </template>
                        <template v-slot:activator="{ on, attrs }" v-else>
                            <v-btn @click="AddAppToQuickActions(app)" icon>
                                <v-icon v-bind="attrs" v-on="on">
                                    mdi-plus-box-outline
                                </v-icon>
                            </v-btn>
                        </template>
                        <span v-if="app.quickActions">Delete from QuickActions</span>
                        <span v-else>Add to QuickActions</span>
                    </v-tooltip>
                    <v-spacer></v-spacer>
                </v-card-actions>
            </v-card>
        </v-col>
    </div>
</template>

<script>
import Dialog from "/var/www/html/weresearchfrontend/src/Dialog.vue";

export default {
    name: "ParentComponent",

    data() {
        return {
            dialog: false,
            selectedApp: null,
            apps: [
                {
                    title: "A",
                    vpnRequired: true,
                    src: "https://source.unsplash.com/user/c_v_r/1900x800",
                    description: "Hello Google",
                    link: "https://google.com",
                    quickActions: true,
                },
                {
                    title: "B",
                    vpnRequired: true,
                    src: "https://source.unsplash.com/user/c_v_r/100x100",
                    description: "Hello Github",
                    link: "https://github.com",
                    quickActions: true,
                },
            ],
        };
    },

    components: {
        Dialog,
    },

    methods: {
        closeDialog: function() {
            this.dialog = false;
            // reset selected app also
            this.selectedApp = app;
        },
    },
};
</script>

CHILD COMPONENT (DIALOG COMPONENT) -

<template>
    <v-dialog :value="value">
        <v-card>
            <v-container fluid>
                <v-row>
                    <v-col fixed style="background:#363636;" class="d-flex" cols="12">
                        <span class="white--text font-weight-bold"
                            >{{ app.title }} > {{ app.description }} > {{ app.link }}</span
                        >
                        <v-spacer></v-spacer>
                        <v-btn plain color="grey" :href="app.link" target="_blank">
                            <v-icon>mdi-open-in-new</v-icon>
                        </v-btn>
                        <v-btn @click="$emit('closeDialog')" plain color="grey">
                            <v-icon>mdi-close-box-outline</v-icon>
                        </v-btn>
                    </v-col>
                    <v-col cols="12">
                        <iframe :src="app.link" width="100%" height="800px" frameborder="0">
                        </iframe>
                    </v-col>
                </v-row>
            </v-container>
        </v-card>
    </v-dialog>
</template>

<script>
export default {
    name: "Dialog",

    props: ["value", "app"],
};
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Angular service worker to pre-fetch video files

Issue arises when the service worker prefetches the entire video embedded on the page, leading to performance problems. My ngsw-config.json only contains configurations for local files, whereas the video is located on a different subdomain under /sites/def ...

When attempting to modify an element in an array within a state-managed object, the input field loses focus

In attempting to address my issue, I have crafted what I believe to be the most concise code example. The main goal is to display a table on the page populated with exercise data retrieved from a database. This data is then assigned to an array of objects ...

It is not possible to apply a background color to an HTML element located outside of the App.Vue

Hey there, thanks for taking the time to read my query! I am interested in adding a gradient background color to a specific page on my Vue application. I tried applying the following code in components/Frontpage.Vue: html { background: linear-gradient( ...

AngularJS JSON Array

I have an array containing multiple objects in JSON, as well as a select element that contains an array. Depending on the selection made in the first array, another select box will be dynamically loaded right below it. HTML Code <div class="list"> ...

Elements that allow for asynchronous data submission without requiring a traditional submit button

Hey there, I could really use some help with this puzzle I'm trying to solve. Here's the situation: <ul> <li>Number: <span id="Number">123</span></li> </ul> I want to set up a connection between t ...

What is the best way to utilize regex to replace two elements simultaneously?

I am facing a challenge with manipulating a string of characters by adding span tags to highlight specific words and change their color. While I have successfully implemented the changes for one pattern using regex, I'm struggling to do the same for a ...

Ways to activate a different jQuery event by utilizing the output from a previously run jQuery event

I have a button on my website that looks like this: <a id="btn" class="button">Click Me!</a> When this button is clicked, it triggers a jQuery function as shown below: $("#btn").on("click", function() { $.ajax({ url: 'index.php&ap ...

Tips for importing all global Vue components in a single file

I currently have a large Vuejs application where I imported all my components globally in the app.js file. While it's functioning well, I believe reorganizing the component imports into a separate file would improve the overall structure of the projec ...

Elements on the page are quivering as a result of the CSS animation

When a user clicks anywhere on a div, I have added a ripple effect to give it some interaction. However, there is an issue where the element shakes and becomes blurry when the page is in full screen mode until the ripple effect disappears. Below is the Ja ...

Troubleshooting Challenges in JavaScript/jQuery Hangman Game

Having trouble with my hangman game's game loop. Attempting to replace correct letters as the game runs through the word, but it's currently: looping through the word checking if the guessed letter is in the word returns index[0] Tried splitti ...

What is the best way to append a single class while also ensuring any previously added classes are removed?

How can I apply the selected class and remove any previously selected classes? <html> <select name="Button-Style" id="Button-Style" style="width:100%;"> <option value="Sun-Flower">Sun Flower</option> <option value ...

Establish a guideline based on a data property for a form input in vueJS

I recently visited a page at https://vuetifyjs.com/en/components/inputs Upon exploring, I discovered the ability to customize rules, such as: rules: [ value => !!value || 'Required.', value => (value || '').length < ...

Javascript: triggering a self-executing function manually

I have a code snippet similar to the following: var msg="first call"; (function test(msg) { console.log("inside self call"); } )(); msg="second call"; console.log("before inline call"); test(msg); console.log("after inline call"); In thi ...

Function that returns an Observable<Boolean> value is null following a catch block

Why is the login status null instead of false in this method? // In the method below, I am trying to return only true or false. isLoggedIn(): Observable<boolean> { return this .loadToken() .catch(e => { this.logger ...

Terminate the rethinkdb data stream

Currently delving into the world of RethinkDB and am eager to utilize the changes() method to fetch a changefeed. While I've figured out how to initiate them, the documentation doesn't quite explain how to halt a changefeed. Is it sufficient to ...

What could be causing Vue Material dialogs to frequently slide off the screen?

I am currently utilizing a dialog feature from Vue Material (). After following the link and opening the first custom dialog on the page, I noticed that it tends to shift towards the top left of my screen, making only a portion of the dialog visible. This ...

Error in Typescript: A computed property name must be one of the types 'string', 'number', 'symbol', or 'any'

Here is the current code I am working with: interface sizes { [key: string]: Partial<CSSStyleDeclaration>[]; } export const useStyleBlocks = ( resolution = 'large', blocks = [{}] ): Partial<CSSStyleDeclaration>[] => { cons ...

Retrieving information through a loop in Next.js

My goal is to utilize the data retrieved from one endpoint to make a call to another endpoint, filtered by ID. I intend to fetch both calls using getServerSideProps and then pass the data to a different component. The initial call will result in an array ...

Having trouble getting my Win and Lose Divs to display in my IF statement within the Card Game

Currently, I am developing a card game where players are presented with a card generated by the computer. The player has to decide whether the next card will be higher, lower, or equal to the current one by clicking corresponding buttons. if ((playerChoic ...

Tips for documenting curried functions using Js docs

I'm encountering issues while trying to use Js docs for generating static documentation of my project. When attempting to run the js doc command, I am faced with numerous parse errors specifically in areas where I have curried functions. Strangely, my ...