0

I am using Vue2 and would like to have a selected-item component that can be reused. It will have a reference to an item that could send a msg on an event bus to set the item to null. This is also my first time working with Vue not in a single-file-compnent / vue file scenario.

I have the following code and have commented the problematic line:

var bus = new Vue()

Vue.component('selected-item', {
  props: ['item'], 
  methods: {
    setToNull(item){
      bus.$emit('setToNull', item);
    }
  },
  template: `<span>
               <div class="button round btn-app-class selected-btn">{{item.name}}<sup><span class='btn-delete link' @click="setToNull(item)">x</span></sup></div>
             </span>   
            `
})

var vm = new Vue({
  template:`
      <div>
        <div v-if="selectedApp">
           <selected-item :item="selectedApp"></selected-item>
        </div>
        <div v-else>
          no app selected
        </div>
      </div>
   `,
    data(){
      return {
        selectedApp: {id: 1, name: 'here is my name'}
      }
    },
    mounted(){
     bus.$on('setToNull', function (item) {
      alert('within setToNull for: ' + item.name); // this works and outputs here is my name 
      item = null; // this doesn't work
    })

    }
})    

What am I doing wrong? Is there a better way to do this?

edit #1

it looks like setting the selectedApp to capture inline the emitted event works! Also removed the bus and some extraneous code. Like so:

      <selected-item @remove="selectedApp = null" :item="selectedApp"></selected-item>

https://jsfiddle.net/qnub8xen/

timpone
  • 19,235
  • 36
  • 121
  • 211
  • why not use `this.selectedApp = null` instead of `item = null`? – Sphinx May 09 '18 at 22:29
  • @Sphinx thx (again) - we will be using with different `items` so want to set to null the item in question. – timpone May 09 '18 at 22:30
  • check [Is JavaScript a pass-by-reference or pass-by-value language?](https://stackoverflow.com/a/3638034/5665870) – Sphinx May 09 '18 at 23:01
  • And you need to adjust your event name, check [Vue Guide: Custom Event](https://vuejs.org/v2/guide/components-custom-events.html#Event-Names), you should always use kebab-case for event names. Then you may not need to create another Vue instance=Bus for event control, just use `this.$emit or this.$on` – Sphinx May 09 '18 at 23:03
  • thx @Sphinx for observations - I updated code at bottom with link to fiddle. This seems to work - is this what you meant? Was not aware of doing it this way! – timpone May 10 '18 at 01:25
  • good job. but you may need to change to `` check [the context will be changed in the compiled template depending on the function scope you are in](https://stackoverflow.com/a/50007405/5665870) – Sphinx May 10 '18 at 16:01

3 Answers3

0

It's this.selectedApp you want to set to null

mounted(){
 bus.$on('setToNull', function (item) {
  alert('within setToNull for: ' + item.name);
  this.selectedApp = null;
})
Sovalina
  • 5,410
  • 4
  • 22
  • 39
  • right - but we want to reuse with different items that support just a name attribute so `selectedApp`, `selectedNetwork` etc.... – timpone May 09 '18 at 22:31
  • @timpone: Then pass the name of the variable you want (and have those variables in a predictable place). JS doesn't support passing by reference, so you can't pass *a variable* -- you end up just passing its value, which basically means you can't null it out in any way the caller will see. – cHao May 09 '18 at 22:52
  • @timpone even if you use `refs`, the problem is from your conditional display `v-if="selectedApp"`. If you want to toggle into the `v-else` you have to set the `selectedApp` to `null`. – Sovalina May 09 '18 at 23:01
0

You could use a callback style instead of event bus and it will let you to reuse your component as many times you want, here is an example:

Vue.component('selected-item', {
  props: ['item', 'callback', 'objName'], 
  methods: {
    setToNull() {
      this.callback(this.objName);
    }
  },
  template: `
    <div v-if="item && item.name">
      <div class="button round btn-app-class selected-btn">
        {{item.name}}
        <span class='btn-delete link' @click="setToNull">x</span>
      </div>
    </div>
  `
})

var vm = new Vue({
  el: '#app',
  template:
  `
  <div>
     <div>
     <selected-item 
           :item="selectedApp"
            :callback="callback"
            :objName="'selectedApp'"
        />
        <selected-item 
           :item="selectedApp2"
            :callback="callback"
            :objName="'selectedApp2'"
        />
        <selected-item 
           :item="selectedApp3"
            :callback="callback"
            :objName="'selectedApp3'"
        />
    </div>
    <div v-else>
     no app selected
    </div>
  </div>
 `,
 data() {
     return {
        selectedApp: {id: 1, name: 'here is my name1'},
        selectedApp2: {id: 2, name: 'here is my name2'},
        selectedApp3: {id: 1, name: 'here is my name3'}
     }
 },
 methods: {
  callback(objName) {
        console.log('object name received on callback ' + objName);
        this[objName] = null;
        console.log('clean value from local parent state');
        console.log(this[objName]);
    }
 }
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app" />
ricardoorellana
  • 2,270
  • 21
  • 35
0

give you an example

'use strict'
const bus = new Vue;

const selectedItem = {
  template: '#selectedItemTpl',
  props: ['item'],
  methods: {
    setToNull(item) {
      bus.$emit('remove-item', item);
    }
  }
}

var app = new Vue({
  el: '#app',
  components: {selectedItem},
  data (){
    return {
      selectedItems: new Array(5).fill('').map((o,i)=>({id:i,name:'item-'+i}))
    }
  },
  created () {
    bus.$on('remove-item',item=>{
      let items = this.selectedItems;
      for(let i=0;i<items.length;i++){
        if(item.id==items[i].id){
          items.splice(i,1)
          break;
        }      
      }
    })
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 <div id="app">
  <div v-for="item in selectedItems" :key="item.id">
   <selected-item :item="item"></selected-item>
  </div>
 </div>

 <script id="selectedItemTpl" type="text/x-template">
  <span>
   <div class="button round btn-app-class selected-btn">{{item.name}}<sup><span class='btn-delete link' @click="setToNull(item)">x</span></sup></div>
  </span>
 </script>
jacky
  • 1,426
  • 6
  • 10