Provoke
Provoke

Reputation: 75

Multiple modal popups vue.js

I am currently using Vue.JS in a research project at work.

At the moment I am working primarily on the front-end side of things. I have a table with a few entries, and on click of the row, I want a modal popup window to appear with further details of that row.

I am just trying to get the multiple popups working, and will work on the dynamic content within each one once this is working correctly.

At the moment the top row is working as seen below:

<tr class="activate-popupmodal-tr" @click="showmodal = true">
                      <td>01/01/2016

                        <modal :show.sync="showmodal">

                          <h3 slot="header">
                            Bug #1 - <span> 04/07/2016 </span>

                            <img src="files/cross.jpg" @click="showmodal = false" />
                          </h3>


                          <div slot="body">
                          </div>

                          <div slot="footer">
                         </div>

                        </modal>
</tr>

With app.js as:

Vue.component('modal', {
  template: '#dashboard-popup-template',
  props: {
    show: {
      type: Boolean,
      required: true,
      twoWay: true
    }
  }
})

new Vue({
  el: '.activate-popupmodal-tr',
  data: {
    showmodal: false
  }
})

This works, but only on one row. Any idea on how I can get this to work?

Table: https://jsfiddle.net/a6n04o3t/

Upvotes: 3

Views: 8604

Answers (1)

Chris Schmitz
Chris Schmitz

Reputation: 20940

Vue is pretty flexible so you can show modals in a number of different ways. Here's one of the ways I use frequently.

A working demo (including some ugly styling) can be found here on webpackbin.

First I start with a parent vue instance that is going to hold my list component, the modal component, and my list of items (really in this case the list of items could be stored in the parent or the list component, but likely you'll want it in the parent):

// in main.js

new Vue({
  el: 'body',
  components:{
    list: List,
    modal: Modal
  },
  data:{
    items:[
      {name: 'foo'},
      {name: 'bar'},
      {name: 'baz'}
    ]
  },
  events:{
    showModal: function(item){
      this.$broadcast('showModal', item)
    }
  }
})

It also holds a bridging event, i.e. an event who's purpose is to listen for $dispatched events coming up from child components and immediately $broadcasts a corresponding event to different child components, passing data along with it if needed. We'll look at this in more detail later.

My index file will contain the placeholders for the list and modal components which will be replaced by each corresponding component's template:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <style>
        ...
    </style>
  </head>
  <body>
    <div class="list-container">
      <list :items="items"></list>
    </div>

    <modal></modal>

    <script src="main.js"></script>
  </body>
</html>

Note the line <list :items="items"></list>. Here we are passing the items from the parent component into the list component.

Then I create my list component that will display my list:

// also in main.js

var List = Vue.component('list',{
  props:['items'],
  template: `
<div class="list-row" v-for="item in items" @click="showModal(item)">
  <span>{{ item.name }}</span>
</div>
`,
  methods:{
    showModal: function(item){
      this.$dispatch('showModal', item)
    }
  }
})

The list component takes in the items as a property so that it can display each item via v-for="item in items" in the list component's template.

For each row that is rendered in the list, I attach a click event that calls the method showModal and passes the current item in to it. That method then dispatches the showModal event up to the parent.

This is where the bridging event on the parent comes in. The parent's showModal event hears the dispatch from the list and immediately broadcasts a showModal event down to all children, passing the current item with it.

The only child that has an event listener for showModal is the modal component:

// also in main.js 

var Modal = Vue.component('modal',{
  template:`
<div class="modal-container" v-show="visible">
  <div class="modal-body">
    <div clas="modal-content">
      {{ item | json }}  
    </div>
    <button @click="closeModal">Close</button>
  </div>
</div>
`,
  data: function(){
    return {
      item: null,
      visible: false
    }
  },
  events:{
    showModal: function(item){
      this.item = item
      this.visible = true
    }
  },
  methods:{
    closeModal: function(){
      this.visible = false
      this.item = null
    }
  }
})

When the modal component detects that showModal has been broadcasted down from the parent, it fires it's showModal event logic which sets the passed in item as a local prop (which gives the modal's template access to it) and the sets the visibility to true.

I do this kind of a pattern to show modals, loading masks, notifications, etc.

Let me know if you have any questions on this approach.

Upvotes: 6

Related Questions