With Vuejs, how to use a modal component inside a v-for loop the right way

15,043

These two links helped me figure this out:

#In your Parent Component You don't have to create a modal for each item within the v-for loop, simply include the modal-component at the beginning of your parent and then work with v-if="..." and props.

<template>
  <div>
    <modal v-if="modalVisible" @close="modalVisible = false" :data="modalData"/>

    <div v-for="item in items">
      <button type="button" @click="openModal(item)">Open Modal</button>
    </div>

  </div>
</template>

and then in your script:

import modal from './Modal'

export default {
  components: {
    modal
  },
  data() {
    return {
      modalVisible: false,
      modalData: null
    }
  },
  methods: {
    openModal(data) {
      this.modalData = data
      this.modalVisible = true
    },
  }

#In your child (modal) component In your modal you can now do the following:

Template:

<template>
    {{ data.foo }}
    <button @click="$emit('close')">Cancel</button>
</template>

Script

<script>
  export default {
    props: ['user']
  };
</script>

Hope that helps :-)

Share:
15,043
Tiago
Author by

Tiago

Updated on July 06, 2022

Comments

  • Tiago
    Tiago almost 2 years

    In my vue.js app, I need to display a list of items which the user can click.

    When clicked, each of these items should then fire a modal, containing additional information about the item that the user just clicked.

    What I have so far in my Items.vue component is:

    <template>
        <div id="content">
            <li v-for="item in items" class="list-item pa2 ba">
                <div class="f5 pt2 pb2">
                    <span>{{item.name}}</span>
                </div>
            </li>
        </div>
    </template>
    
    <script>
        import Items from '@/api/items';
    
        export default {
            name: 'items',
            asyncComputed: {
                items: {
                    async get() {
                        const items = await Items.getAll();
                        return items.data;
                    },
                    default: []
                }
            },
            components: {}
        }
    </script>
    

    Now, I could simply add a modal component to the v-for loop, thus creating one modal for each item, but this does not seem ideal if, for example, I have a list of thousands of items.

    This led me to believe that the modal should probably be placed at the root of the app (in my case App.vue), like this:

    <template>
        <div id="app">
            <modal></modal>
            <router-view></router-view>
        </div>
    </template>
    
    <script>
        export default {
            name: 'app'
        }
    </script>
    

    and then somehow fired with custom data whenever I needed it.

    However, I'm not sure how to proceed. How do I fire this modal with custom information from inside the v-for loop, which is in a child component relative to App.vue?

  • Scornwell
    Scornwell over 5 years
    This was fantastic, I was racking my head on this for a few hours. Simply moving it out of the for loop and passing it data solved my issue of having a modal pop-up for every line item regardless of which on was clicked. +1