2

I have some li elements in a v-for cicle. I need to select more then one at the same time and, if already selected, remove active class.

My script works if I select the items in order: from first to last (to select items), from last to first (to deselect items). Please help me.

var example1 = new Vue({
  el: '#example-1',
  data: {
    tags: ['tag-1', 'tag-2', 'tag-3', 'tag-4'],
    activeTag: [],
  },
  methods: {
    onTagClick: function(i) {
      if ( this.activeTag.includes(i) ) {
        console.log('Delete');
        const index = this.activeTag.indexOf(i);
        if ( index > -1 ) {
            this.activeTag.splice(index, 1);
        }
      } else {          
        console.log('Add');          
        this.activeTag.push(i);
      }
      console.log(`activeTag[i]: ${this.activeTag}`);
    }
  }
})
li {
  display: inline-block;
  padding: 8px 10px;
  margin-right: 0.5rem;
}

a {
color: #000;
  text-decoration: none;
}

li.active>a{
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="example-1">
  <ul v-if="tags">
    <li v-for="(tag, i) in tags" :key="i" :class="{active: activeTag[i] === i}">
        <a @click="onTagClick(i)"  href="javascript:void(0)">{{ tag }}</a>
    </li>
</ul>
</div>

2
  • Hi, did my answer helped somehow ? Commented Apr 23, 2021 at 6:42
  • 1
    Hi @kissu, I used Vinicius code and your advice to map the array. Thank you very much Commented Apr 23, 2021 at 7:26

3 Answers 3

2

What do you think about this solution? It's the shortest way to achive the same result.

var example1 = new Vue({
  el: '#example-1',
  data: {
    tags: [{tag:'tag-1', active: false},{tag:'tag-2', active: false}, {tag:'tag-3', active: false}, {tag:'tag-4', active: false} ],
    
  },
  methods: {
    onTagClick: function(i) {
       this.tags[i].active = !this.tags[i].active ;
    }
  }
})
li {
  display: inline-block;
  padding: 8px 10px;
  margin-right: 0.5rem;
}

a {
color: #000;
  text-decoration: none;
}

li.active>a{
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="example-1">
  <ul v-if="tags">
    <li v-for="(obj, i) in tags" :key="i" :class="{active: obj.active}" >
        <a @click="onTagClick(i)"  href="javascript:void(0)">{{ obj.tag }}</a>
    </li>
</ul>

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

Comments

1

template

<li v-for="(tag, index) in tags" :key="index" :class="{active: tag.active}">
    <a href="#" @click.prevent="tag.active = !tag.active">{{ tag.name }}</a>
</li>

Script

  computed: {
    checked(){ return this.tags.filter(tag => tag.active).map(tag => tag.name)}
  },
  data() {
    return {
      tags: [
        {name:'tag-1', active: false},
        {name:'tag-2', active: false},
        {name:'tag-3', active: false},
        {name:'tag-4', active: false}
      ]
    }
  }

Complete sample. Check this out

<template>
  <ul v-if="tags">
    <li v-for="(tag, index) in tags" :key="index" :class="{active: tag.active}">
      <a href="#" @click.prevent="tag.active = !tag.active">{{ tag.name }}</a>
    </li>
  </ul>
  <div>
    {{checked}}
  </div>
</template>

<script>
import { defineComponent } from "vue";
export default defineComponent({
  computed: {
    checked(){ return this.tags.filter(tag => tag.active).map(tag => tag.name)}
  },
  data() {
    return {
      tags: [
        {name:'tag-1', active: false},
        {name:'tag-2', active: false},
        {name:'tag-3', active: false},
        {name:'tag-4', active: false}
      ]
    }
  }
});
</script>
<style>
li {
  display: inline-block;
  padding: 8px 10px;
  margin-right: 0.5rem;
}

a {
color: #000;
  text-decoration: none;
}
li.active>a{
  color: red;
}
</style>

4 Comments

I cannot change data structure
@riky1 why can't you change the data structure ? I don't see any reason of it not being feasable.
I get data from DB and I have that in props. I put values in data() only for the example
You can totally get those values from a DB and massage it. Check my updated answer. Props or data do not make any difference, it's still data that you can change in your component, not a big deal.
1

Here is my solution

<template>
  <div>
    <span
      v-for="element in array"
      :key="element.id"
      :class="{ red: element.active }"
      @click="toggleSelection(element)"
    >
      {{ element.label }}
    </span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      array: [
        { id: 1, label: 'tag1', active: false },
        { id: 2, label: 'tag2', active: false },
        { id: 3, label: 'tag3', active: false },
      ],
    }
  },
  methods: {
    toggleSelection(element) {
      const selectedElementIndex = this.array.findIndex((item) => item.id === element.id)
      this.array[selectedElementIndex].active = !this.array[selectedElementIndex].active
    },
  },
}
</script>

<style scoped>
.red {
  color: red;
}
</style>

I still highly recommend updating your array and giving it a real unique ID, rather than passing index to the :key, which is actually doing the opposite of what it's supposed to do.

To achieve a friendly array with nice keys, you can use this on your current tags:

this.array = tags.map((tag, index) => ({id: index, label: tag, active: false}))

You could call this method once you have pulled the data from an API (in an async created() hook), change it then insert it into your template, hence modify the structure to a more friendly format.

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.