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

When a previous form field is filled, validate the next 3 form fields on keyup using jQuery

Upon form submission, if the formfield propBacklink has a value, the validation of fields X, Y, and Z must occur. These fields are always validated, regardless of their values, as they are readonly. An Ajax call will determine whether the validation is tru ...

What is the appropriate way to utilize `render_template` from Flask within Angular?

I'm facing an issue where I need to implement different routes for various Angular Material tabs. I have attempted to directly call Flask from the template, as demonstrated below, but unfortunately, I am unable to invoke render_template from Angular. ...

Baconjs exclusively retrieves the final debounce value

Below is a code snippet that showcases my current implementation: let watcher; const streamWatcher = bacon.fromBinder(sink => { watcher = chokidar.watch(root, { ignored: /(^|[\/\\])\../ }); watcher.on('all&a ...

Loop through an HTML table in order to emphasize variations in cells that contain multiple comparison items

I am interested in highlighting variances between the initial row of a table and all other rows based on columns. I have successfully managed to achieve this when each cell contains only one item/comparison. However, I would like to expand this to include ...

Merge JSON objects into an array

Is there a way to merge JSON objects when the initial object is: { "total": "2" } And the second one is: [ "player1": { "score": "100", "ping": "50" }, "player2": { "score": "100", "ping": "50" ...

ajax with names that are alike

I've set up a text input field that searches for file names on my server. However, it currently requires an exact match to display the file. I'm looking for a solution that can show me files even if the input text isn't an exact match. Here ...

Limit access to Google Fusion Table to specific types of maps. Eliminate Google Fusion Table for selected map formats

Currently, I am in the process of creating a web map using the Google Maps Javascript API. My objective is to display a Google Fusion Table containing buildings in Boston exclusively on a stylized map named "Buildings." When I switch to the Buildings map t ...

Using PHP to ascertain the requested dataType or responseType from the client

My ajax request is fairly simple: $.post('server.php',data, function (json) {console.log(json)},'json'); I have configured jQuery to expect json data based on the dataType setting. Question: Is the dataType parameter equivalent to re ...

Difficulty Encountered in Rendering Component Using setTimeout Function

Having trouble figuring out why my component, enclosed in a setTimeout function, is not appearing on the DOM: const ContentMain = Component({ getInitialState() { return {rendered: false}; }, componentDidMount() { this.setStat ...

Using Node.js in conjunction with Nuxt.js: a beginner's guide

I have a server.js file located in the "Server" directory, which is connected to Nuxt.js server.js const express = require('express'); const app = express(); app.get('/api/data', (req, res) => { res.json({ message: 'Hello fr ...

Retrieving all buttons from a webpage and removing those with a specific background-image using JavaScript

Hey everyone, I'm still learning about javascript and other web development languages. My current task is to locate all the buttons on a webpage and remove the ones that have a specific background image. I know about using getElementsByTagName but n ...

In a jQuery project, WebStorm marks all $-operators as "unrecognized."

Just a quick question from a beginner: I'm facing an issue where my WebStorm IDE doesn't recognize any jQuery code, even though the webpage functions correctly in the browser. Here's what I've done so far: I have installed WebStorm V ...

Navigating through content on a screen can be accomplished in two

Is it feasible to incorporate both horizontal and vertical scrolling on a website using anchor tags? I recently created a website with horizontal scrolling for each main page. Within some of these main pages, there are subsections that I would like to hav ...

unable to use ref to scroll to bottom

Can someone explain to me why the scroll to bottom feature using ref is not functioning properly in my code below? class myComponent extends Component { componentDidMount() { console.log('test') // it did triggered this.cont ...

Extracting information from dynamically generated tables using Python 2.7, Beautiful Soup, and Selenium

I am in need of assistance with scraping a JavaScript generated table and saving specific data to a csv file. The tools available to me are limited to python 2.7, Beautiful Soup, and/or Selenium. Although I have referred to the code provided in question 14 ...

How come this variable isn't recognized as 0 even though the debugger is indicating otherwise?

I am currently working on a React component that receives the total number of reviews as a prop. In cases where the number of reviews is 0, I intend to display an element indicating <div>No reviews yet.</div> If there are reviews available, I ...

``Why is my Vue.js ag-Grid filter component failing to function?

I have recently started working with vue-js and ag-grid, and I wanted to implement a custom filter on my ag-grid. I attempted to use a component as a filter based on the example provided in the vue-js ag-grid documentation: "https://www.ag-grid.com/javascr ...

The dropdown item in Tailwindcss is unexpectedly flying off the edge of the screen rather than appearing directly under the dropdown button

Currently, I am developing an application using Rails, Vue, and TailwindCss 1.0+ I am facing an issue while creating a dropdown menu for my products. When I click on the dropdown button, the items in the dropdown fly off to the edge of the screen instead ...

Troubleshooting peerDependencies issue in Laravel 9 while installing ReactJS and VueJS with laravel/ui

I added Laravel/Ui to a Fresh Laravel 9 installation for setting up ReactJs I followed these commands step by step in the command line interface: composer require laravel/ui php artisan ui react npm install After running the npm install command, I en ...

Sending an AJAX request from one subdomain to another subdomain within the same domain

Is it true that cross-domain ajax requests require a 'proxy' server to be used? But what if an ajax request is made from server1.example.com to server2.example within the same domain of example.com? Would that still not work? I've noticed ...