alittlecurryhot
alittlecurryhot

Reputation: 487

Toggle Class for individual elements in Vue JS

I have a todo list that is generated via jsonplaceholder , the button on click populates the div with dummy content.

What I am trying to achieve is that when I click on an individual div(task) it should be removed/hidden.

So far,

<button id="btn" v-on:click= 'getTodo'>Show Todo</button>
    <div id="jsonData" v-for = 'todo in todos' >
        <ul v-bind:class = '{active: isActive}' v-on:click = 'removeTask'>
            <li>{{todo.title}} </li>
            <li id="status"> Task Status : {{todo.completed}} </li>
        </ul>
    </div>

JS

var vm =  new Vue({
el: '#app',
data() {
    return {
        todos: [],
        isActive: false
    }
},
methods: {
    getTodo: function() {
        axios.get('https://jsonplaceholder.typicode.com/todos')
        .then((response) => {
            this.todos = response.data;
        })
    },
    removeTask: function() {
        this.isActive = !this.isActive;
    }
}

})

The 'removeTask' event handler toggles the classes for all the generated divs(tasks) and not to the clicked div.

How do I add/remove the class on the clicked div?

Upvotes: 0

Views: 1882

Answers (2)

Francis Leigh
Francis Leigh

Reputation: 1960

Using the axios API i get the todos and pragmatically loop over the individual todos and apply an isActive prop to all of them. These props can now be toggled via toggleTodo when passing the todo' id to the method. :-)

Notice i have applied a v-if="todo.isActive" upon the ul this can also be translated to :class="{active: todo.isActive}" if you were wanting to apply some styling i.e for transitions etc.

By using this approach you can use the same logic to create methods such as completeTodo and removeTodo etc

I have added a showList prop in order to show/hide the actual full list.

I noticed you are using v-bind and v-on - this is absolutely valid but VUE ^2.0 can handle shorthand binding :-)

I also noticed you are using single quotes for your HTML attributes... Although this does not break your markup i think it's best practice to keep them as double quotes.

var vm =  new Vue({
  el: '#app',
  data: {
    todos: [],
    showList: false
  },
  mounted () {
    this.getTodos()
  },
  methods: {
    getTodos () {
      axios.get('https://jsonplaceholder.typicode.com/todos')
      .then(res => {
        res.data = res.data.map(todo => {
          todo.isActive = false
          return todo
        })

        this.todos = res.data
      })
    },
    showTodos () { this.showList = !this.showList },
    toggleTodo (id) {
      this.todos = this.todos.map(todo => {
        if (todo.id === id) {
          todo.isActive = !todo.isActive
        }
        return todo
      })
    }
  }
})
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>


<div id="app">
  <button @click="showTodos()">{{this.showList ? 'Hide' : 'Show'}} Todos</button>
  <div v-if="todos.length && showList" v-for="todo in todos">
    <button id="btn" @click="toggleTodo(todo.id)">Toggle Todo</button>

    <ul v-if="todo.isActive" :class="{active: todo.isActive}">
      <li>{{todo.title}} </li>
      <li id="status"> Task Status : {{todo.completed}} </li>
    </ul>
  </div>
</div>

<style lang="css">
  .active {
    border: 2px solid green;
  }
</style>

Upvotes: 3

Phiter
Phiter

Reputation: 14992

isActive is one for the entire instance, not every individual todo.

What you'll need to do is to to populate the todo list either by mapping the response data or simply use the response data's completed property.

{
    "userId": 10,
    "id": 193,
    "title": "rerum debitis voluptatem qui eveniet tempora distinctio a",
    "completed": true
}

Every item of the response's todo has the completed prop, so you can use that.

That's how you'd go about doing it:

var vm = new Vue({
  el: '#app',
  data() {
    return {
      todos: []
    }
  },
  methods: {
    getTodo: function() {
      axios.get('https://jsonplaceholder.typicode.com/todos')
        .then((response) => {
          this.todos = response.data;
        })
    },
    toggleCompleted: function(todo) {
      todo.completed = !todo.completed;
    }
  }

})
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
  <button id="btn" v-on:click='getTodo'>Show Todo</button>
  <div id="jsonData" v-for='todo in todos'>
    <ul v-bind:class='{active: todo.completed}' v-on:click='toggleCompleted(todo)'>
      <li>{{todo.title}} </li>
      <li id="status"> Task Status : {{todo.completed ? 'Completed' : 'Active'}} </li>
    </ul>
  </div>
</div>

Upvotes: 1

Related Questions