1

I'm trying to create a vue component but whenever I want to hide some elements with v-show it doesn't work.

When you click any element on the list I want to hide it and on the click event element.visible is set to false, so in the component template I bind that value to v-show but it wont hide.

I guess it's because element.visible it's kind of nested attribute but I'm not really sure.

var collection = [
        { id: 1, name: 'element 1' },
        { id: 2, name: 'element 2' },
        { id: 3, name: 'element 3' },
        { id: 4, name: 'element 4' },
        { id: 5, name: 'element 5' },
        { id: 6, name: 'element 6' },
        { id: 7, name: 'element 7' },
        { id: 8, name: 'element 8' },
      ];

var multiselect = {
  props: ['collection'],
  data: function() {
    return {
      subscribed: [],
      toSubscribe: [],
      toUnsubscribe: [],
      dataset: []
    }
  },
  mounted: function(){
  	this.dataset = _.map(this.collection, function(element){
    	element.visible = true;
      return element;
    });
  },
  methods: {
    subscribe: function(element){
			element.visible = false;
    }
  }

}

new Vue({
  el: '#app',
  components: {
    'multiselect': multiselect
  },
  data: {
    elements: collection
  }
})
.multiselect .list {
  border: 1px solid #000;
  height: 215px;
  max-height: 215px;
  overflow: scroll;
}
.multiselect .list .list-element {
  text-align: center;
  padding: 0.2em;
  cursor: pointer;
}
.multiselect .list .list-element:hover {
  background-color: #d6dbdf;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>



<div id="app">

  <multiselect inline-template :collection="elements">
    <div class="col-sm-12 multiselect">

      <div class="col-sm-5 list">
        <div class="col-sm-12">
          <div v-for="element in dataset" class="list-element" @click="subscribe(element)" v-show="element.visible">
            {{element.name}}
          </div>
        </div>
      </div>

      <div class="col-sm-2">
        <button class="btn btn-primary btn-fill">
          <i class="fa fa-arrow-right" aria-hidden="true"></i>
        </button>

        <button class="btn btn-primary btn-fill">
          <i class="fa fa-arrow-left" aria-hidden="true"></i>
        </button>
      </div>

      <div class="col-sm-5 list">

      </div>

    </div>
  </multiselect>


</div>

0

2 Answers 2

2

As an interesting variation, you don't need to clone the collection elements or set a property on them.

It is enough to have a parallel array of flag, but you have to be careful of the syntax to update them and the flag must be contained in an object in order to be observable.
i.e an array of { visible: true } rather than an array of true.

Ref: Mutation-Methods

var collection = [
  { id: 1, name: 'element 1' },
  { id: 2, name: 'element 2' },
  { id: 3, name: 'element 3' },
  { id: 4, name: 'element 4' },
];

var multiselect = {
  props: ['collection'],
  data: function() {
    return {
      visibleFlags: []
    }
  },
  created: function(){
    this.collection.forEach(x => {
      this.visibleFlags.push({visible: true});  // Vue mutation method
    })
  },
  methods: {
    subscribe: function(index){
      this.$set(this.visibleFlags, index, false)
    }
  }
}

new Vue({
  el: '#app',
  components: {
    'multiselect': multiselect
  },
  data: {
    elements: collection
  }
})
.multiselect .list {
  border: 1px solid #000;
  height: 125px;
  max-height: 215px;
  overflow: scroll;
}
.multiselect .list .list-element {
  text-align: center;
  padding: 0.2em;
  cursor: pointer;
}
.multiselect .list .list-element:hover {
  background-color: #d6dbdf;
}
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <multiselect inline-template :collection="elements">
    <div class="col-sm-12 multiselect">
      <div class="col-sm-5 list">
        <div class="col-sm-12">
          <div v-for="(element, index) in collection" 
            class="list-element" v-show="visibleFlags[index].visible" 
            @click="subscribe(index)">
            {{element.name}} 
          </div>
        </div>
      </div>
    </div>
  </multiselect>
</div>

Sign up to request clarification or add additional context in comments.

Comments

1

The problem is that you are modifying an already-responsive object. Vue cannot detect property additions.

It's obscured by the fact that you're copying via map, and assigning it to a new array, but it's an array of references to responsive objects, to each of which you have added the visible property. If you examine the data items in the parent, you'll see that it gets visible added, too.

The minimal fix is to use Object.assign to create a new object and copy properties into it. This way all properties are inserted into a non-responsive object, which is then made responsive during assignment.

  mounted: function(){
    this.dataset = _.map(this.collection, function(element){
      return Object.assign({}, element, {visible: true});
    });
  },

You could do this in created, since you don't need the DOM element.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.