Search code examples
javascriptarraysvue.jsfindvuex

How to toggle a todo completed option?


I'm expecting clicking the Done/Undone individual task button to toggle the task data completed to opposite using Vuex store actions. But I'm getting the 404 error. Even though I'm passing the task id from the child component. Or should I do it with mutations?

https://stackblitz.com/edit/nuxt-bootstrap-vue-dynamic-img-bkwixg?file=store%2Findex.js,components%2FTask.vue

// Child component

<template>
  <div :class="{ task: task.completed }">
    <p class="content">
      {{ task.todo }}
    </p>
      <button @click="toggleTask">
        {{ task.completed ? 'Undone' : 'Done' }}
      </button>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  props: ['task'],
  methods: {
    toggleTask(id) {
      this.$store.dispatch('toggleTask', id);
    },
  },
};
</script>

// Parent component

<template>
      <Task v-for="task in tasks" :key="task.id" :task="task" />
</template>

<script>
export default {
  data() {
    return {
      todo: '',
      completed: false,
      search: '',
    };
  },
  computed: {
    tasks() {
      return this.$store.getters.getTasks(this.search);
    },
  },
  mounted() {
    this.$store.dispatch('getTasks').then((data) => console.log(this.tasks));
  },
};
</script>

// Store 

export const state = () => ({
  tasks: [],
});

export const actions = {
  async getTasks(context) {
    const res = await fetch('https://dummyjson.com/todos/user/5');
    if (res.ok) {
      let result = await res.json();
      context.commit('setTasks', result.todos);
    }
    return res.ok;
  },
  async toggleTask(context, id) {
    const res = await fetch(`https://dummyjson.com/todos/${id}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
      },
      // body: JSON.stringify(data),
    });
    if (res.ok) {
      const tasks = (context.state.tasks.find(
        (task) => task.id === id
      ).completed = !completed);
      context.commit('setTask', id);  // or should I commit to toggle task mutation and how can I do it ?
    }
    return res.ok;
  },
};

export const mutations = {
  setTasks(state, data) {
    state.tasks = data;
  },
  toggleTask(state, id) {
    state.tasks.find((task) => task.id === id).completed = !completed;
  },
};

export const getters = {
  },
};


Solution

  • First, You pass task to method:

    <button @click="toggleTask(task)">
      {{ task.completed ? 'Undone' : 'Done' }}
    </button>
    

    then in Vuex create action that will put data and commit mutation:

    async toggleTask(context, task) {
      const res = await fetch(`https://dummyjson.com/todos/${task.id}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json;charset=utf-8',
        },
        body: JSON.stringify({completed: !task.completed}),
      })
      let result = await res.json();
      context.commit('updateTask', result)
    },
    

    at the end mutation:

    updateTask(state, task) {
      state.tasks = [
        ...state.tasks.map(item => 
          item.id !== task.id ? item : {...item, ...task}
        )
      ] 
    },