Lahori
Lahori

Reputation: 1091

Creating Component Dynamically always removes last instance

I have a child component and parent component. Parent component renders child component dynamically i.e. on demand and keeps the record in array. When a child component demands to be removed, it emits event and hence passes down its id to be identified in record. Though the record does get removed based on id but the last created instance is removed, always. Even if you click the first child, it is going to remove the last one only.

Here is the link that is identical to my situation but only in simple form. I did research on SO and found this answer whose fiddle is here. So I did follow its pattern in another fiddle but result is no different.

I don't know whats the problem here... what I am doing wrong?

Update 1: Added Code

Update 2: Check first link if you want to skip below

ChatPanel.vue

 <template>
  <div class="chat-container">
    <div class="columns" style="justify-content: flex-end;">
      <div class="column is-3" style="order: 1;">
        <div class="chat-panel">
          <nav class="panel state" :class="[statusIn ? 'in' : 'out']">
            <p class="panel-heading">
              Arbab Nazar
              <span id="click-handle" @click="toggleState"></span>
            </p>
            <div class="panel-block">
              <p class="control has-icons-left">
                <input class="input is-small" type="text" placeholder="search">
                <span class="icon is-small is-left">
                  <i class="fa fa-search"></i>
                </span>
              </p>
            </div>
            <p class="panel-tabs">
              <a class="is-active">all</a>
              <a>Online</a>
              <a>Sleeping</a>
            </p>

            <chat-list/>
          </nav>
        </div>
      </div>

       <chat-window v-for="(window, index) in windows" 
                    :identity="index" 
                    v-on:remove-window="removeWindow(window)"
      /> 

    </div>
  </div>
</template>

<script>
import ChatList from './ChatList'
import ChatWindow from './ChatWindow'

import Bus from '../Events/Bus.js'

export default {
  name: 'chatpanel',
  data () {
    return {
      statusIn: true,
      windows: [],
      id: Number
    }
  },
  mounted() {
    Bus.$on('new-window', (data)=> {
      this.windows.push((this.windows.length+1))
    })
  },
  methods: {
    toggleState(event) {
      event.stopPropagation()
      this.statusIn = !this.statusIn

    },
    removeWindow(window) {
      this.windows.splice(this.windows.indexOf(window),1)
    }
  },
  components: {
    ChatList,
    ChatWindow
  }
}
</script>

ChatWindow.vue

<template>
    <div class="column is-2">
        <div class="chat-window-container" :class="{'reset': statusIn}">
            <div class="card state" :class="[statusIn ? 'in' : 'out']">
                <header class="card-header">
                    Ahmad Jan
                    <a class="delete" @click="$emit('remove-window')"></a>
                    <span id="click-handle" @click="toggleState"></span>
                </header>
                <div class="card-content">
                    <template v-for="message in messages">
                        <p>
                            {{ message }}
                        </p>
                    </template>
                </div>
            </div>
            <div class="field has-addons">
                <div class="control is-expanded">
                    <input class="input" type="text" 
                            placeholder="Write something amazing..."
                            @keyup.enter="sendMessage" v-model="messageText"
                    >
                </div>
                <div class="control">
                    <a class="button is-primary" 
                        @click="sendMessage"
                        style="background:rgb(0, 184, 255)"
                    >
                    Send
                    </a>
                </div>
            </div>

        </div>
    </div>
</template>
<script>
import Bus from '../Events/Bus.js'

export default {
    props:['identity'],
    data() {
        return {
            messageText: '',
            messages: [],
            statusIn: true,
            id: ''
        }
    },
    created() {
        this.id = this.identity
    },
    methods: {
        sendMessage(event) {
            this.messages.push(this.messageText)
            this.messageText = ''
            console.log('msg', event.target.value)
            // this.messages
        },
        toggleState(event) {
            event.stopPropagation()
            this.statusIn = !this.statusIn

        },
        removeWindow(id) {
            console.log(`remove window with id ${id}`)
            Bus.$emit('remove-window', {id})
        }
    }
}
</script>

ChatList.vue

<template>
    <div style="overflow-y: scroll;max-height: 17.5rem;">
        <template v-for="chat in chats">
            <chatter :user="chat"></chatter>
        </template>
    </div>
</template>
<script>
import Chatter from './Chatter'

export default {
    props:{},
    data () {
        return {
            chats: [
                { name: 'Abdul Hameed', active: true  },
                { name: 'Ahmad Jan', active: false }, 
                { name: 'Murad Surkhab', active: false }, 
                { name: 'Angelo Mathews', active: false }, 
                { name: 'Hasan Ali', active: true }, 
                { name: 'Fakhar-ud-Din', active: true }, 
                { name: 'Sultan Usman', active: true }, 
                { name: 'Muad Saeed', active: false }, 
                { name: 'Saleem Yousaf', active: false }]
        }
    },
    components: {
        Chatter
    }
}
</script>

Chatter.vue

<template>
    <div>
        <a class="panel-block" :class="{'is-active':user.active }" @click="letsCaht">
            <div style="display: flex;">
                <p class="image is-24x24 chat-image" >
                    <img src="http://bulma.io/images/placeholders/96x96.png">
                </p>
                <p class="content">
                    {{user.name}}
                </p>
            </div>
            <span class="panel-icon">
                <i class="fa fa-comments"></i>
            </span>
        </a>
    </div>
</template>
<script>
import Bus from '../Events/Bus.js'

export default {
    props:['user'],
    methods: {
        letsCaht(event) {
            Bus.$emit('new-window', {user: this.user})
            console.log(`${this.user.name} is listening`)
        }
    },
    components: {}
}
</script>

Upvotes: 2

Views: 1652

Answers (2)

梁宇荣
梁宇荣

Reputation: 1

Every time you add a new component, you should add a unique id for the new component.

this is work for me .

======================================================================

myradio.vue


<template>
    <div>
         <div>
             <label>{{inputVal}}</label>
         </div>
    </div>
</template>

<script>
export default {
  name: 'myradio',
  data () {
    return{
      inputVal:new Date().getTime()//Get timestamp
    }
  }

}
</script>


mysurvey.vue


<template>
    <div>

        <div>
            <button v-on:click="addNewRadio">addNewRadio</button>
        </div>
        <hr>
         <div v-for="(item,index) in radioComponents" :key="item.myid">
             <li v-bind:is="item.myComponent"></li>
             <button v-on:click="delRadio(index)">delRadio</button>
             <hr>
         </div>
    </div>
</template>

<script>

import myradio from '@/myComponent/myradio'

export default {
  name: 'mysurvey',
  components: {
    myradio
  },
  data () {
    return {
      radioComponents: [
        {
          myid:1,
          myComponent:myradio
        },
        {
          myid:2,
          myComponent:myradio
        }
      ]
    }
  },
  methods: {
    addNewRadio:function(){
      let size=this.radioComponents.length+1;
      var json={
        myid:size,
        myComponent:myradio
      };
      this.radioComponents.push(json);
    },
    delRadio:function (index) {
      this.radioComponents.splice(index, 1);
    }
  }

}
</script>

Upvotes: 0

AWolf
AWolf

Reputation: 8970

I think your deleting is working in the first fiddle but you're not seeing it correctly because your list is getting shorter if you're deleting one item. So it seems that always the last item is removed.

Also adding an id to your object helps Vue to render the v-for and you can add the key binding to id.

Please have a look at the demo below or this fiddle.

In your application code that you posted in your question:

Is your remove handler called? You're emitting on bus but your listener is attached to this. Please have a look at this fiddle so you're seeing the difference.

Vue.component('child', {
		props:['index', 'data'],
    template: `
        <div>
            data# {{data}}
            <button @click="$emit('delete-me')">Delete</button>
        </div>`
})

Vue.component('parent', {
    template: `
        <div>
            Keep Adding new Instances 
            <button @click="newChild">New</button>
            <hr />
            <child v-for="(row, index) in children" :data="row" 
            v-on:delete-me="deleteThisRow(index)" :key="row.id"
            :index="index"
            ></child>
        </div>`,
        data() {
        	return {
            id: 0,
          	children:[]
          }
    },
    methods: {
    	newChild() {
      	this.children.push({
        	id: this.id++,
        	value: 'new child'
        })
      },
      deleteThisRow(index) {
      			// console.log('index', index, this.children)
            this.children.splice(index, 1);
        }
    }
})

new Vue({
    el: '#app',
    template: `
        <div>
        		<parent />
            
        </div>
    `,
    
    methods: {
        
    }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<div id="app"></div>

Upvotes: 3

Related Questions