Search code examples
javascriptvue.jsobjectpropertiesvuex

How to change the property of an object in a loop using a checkbox in vuex


I have a todosModule which contains the necessary state. The todos array contains the objects. When I output them through the loop, I need the completed property of a specific object in the loop to change when the checkbox changes.

Completed property is boolean.

Down below is file todosModule.js for store (vuex)

import axios from "axios";
export const todosModule = {
    state: () => ({
        todos: [],
        page: 1,
        limit: 10,
        totalPages: 0,
        isTodosLoading: false
    }),
    getters: {

    },
    mutations: {
        setTodos(state, todos) {
            state.todos = todos
        },
        setPage(state, page) {
            state.page = page
        },
        setTotalPages(state, totalPages) {
            state.totalPages = totalPages
        },
        setLoadingTodos(state, bool) {
            state.isTodosLoading = bool
        },
        setCompleted(state, completed) {
            console.log(state.todos.completed)
            state.todos.completed = completed
        }

    },
    actions: {
        async fetchTodos({state, commit}) {
            try {
                commit('setLoadingTodos' , true)
                const response = await axios.get('https://jsonplaceholder.typicode.com/todos', {
                    params: {
                        _page: state.page,
                        _limit: state.limit
                    }
                })
                commit('setTotalPages', Math.ceil(response.headers['x-total-count'] / state.limit))
                commit('setTodos', response.data)
            }
            catch (e) {
                console.log(e)
            }
            finally {
                commit('setLoadingTodos', false)
            }
        }
    },
    namespaced: true
}

Down below is TodoItem.vue

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
  <div class="col">
    <div class="card">
      <div class="card-body" :class="todo.completed ? 'bg-light' : ''">
        <div class="d-flex align-self-center justify-content-between">
          <h5 class="card-title">{{todo.title}}</h5>
          <div class="form-check form-switch">
            <input class="form-check-input" type="checkbox" id="flexSwitchCheckChecked" @change="setCompleted" :checked="todo.completed">
          </div>
        </div>
        <p v-if="todo.completed === true" class="card-text text-success">Выполнено</p>
        <p v-else class="card-text text-danger">Невыполнено</p>
        <div class="d-flex align-content-center justify-content-between">
          <div class="btn-list">
            <button-bootstrap css-class="btn-primary">Изменить</button-bootstrap>
            <button-bootstrap css-class="btn-danger">Удалить</button-bootstrap>
          </div>
          <div class="card-date d-inline-flex text-muted">
            <span class="align-self-center">id {{todo.id}}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import ButtonBootstrap from "@/components/UI/ButtonBootstrap";

export default {
  name: "TodoItem",
  components: {ButtonBootstrap},
  props: {
    todo: {
      type: Object,
      required: true
    }
  },
  methods: {
    setCompleted(event) {
      this.$store.commit('todos/setCompleted', event.target.checked)
    },
  }
}
</script>

<style lang="scss" scoped>
.form-switch .form-check-input {
  margin-left: 0;
}
.btn-list {
  button:first-child {
    margin-right: 1rem;
  }
}
</style>

Parent file TodoList.vue

<template>
<div class="row row-cols-1 g-4">
  <TodoItem v-for="todo in todos" :todo="todo" :key="todo.id"/>
</div>
</template>

<script>
import TodoItem from "@/components/TodoItem";
export default {
  name: "TodoList",
  components: {TodoItem},
  props: {
    todos: {
      type: Array,
      required: true,
    }
  }
}
</script>

<style lang="scss" scoped>
.row {
  margin-top: 2rem;
}
</style>

Help please, how to change a specific property of a specific object?

UPDATE! I fount a solution!

setCompleted(state, completed) {
            const index = state.todos.findIndex(todo => todo.id === completed.id);
            state.todos[index].completed = completed.completed
        }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
  <div class="col">
    <div class="card">
      <div class="card-body" :class="todo.completed ? 'bg-light' : ''">
        <div class="d-flex align-self-center justify-content-between">
          <h5 class="card-title">{{todo.title}}</h5>
          <div class="form-check form-switch">
            <input class="form-check-input" type="checkbox" id="flexSwitchCheckChecked" @change="setCompleted($event, todo.id)" :checked="todo.completed">
          </div>
        </div>
        <p v-if="todo.completed === true" class="card-text text-success">Выполнено</p>
        <p v-else class="card-text text-danger">Невыполнено</p>
        <div class="d-flex align-content-center justify-content-between">
          <div class="btn-list">
            <button-bootstrap css-class="btn-primary">Изменить</button-bootstrap>
            <button-bootstrap css-class="btn-danger">Удалить</button-bootstrap>
          </div>
          <div class="card-date d-inline-flex text-muted">
            <span class="align-self-center">id {{todo.id}}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import ButtonBootstrap from "@/components/UI/ButtonBootstrap";

export default {
  name: "TodoItem",
  components: {ButtonBootstrap},
  props: {
    todo: {
      type: Object,
      required: true
    }
  },
  methods: {
    setCompleted(event, id) {
      this.$store.commit('todos/setCompleted', {completed: event.target.checked , id: id});
    },
  }
}
</script>

<style lang="scss" scoped>
.form-switch .form-check-input {
  margin-left: 0;
}
.btn-list {
  button:first-child {
    margin-right: 1rem;
  }
}
</style>


Solution

  • All you have to do is pass an additional attribute to the method

    <input class="form-check-input" type="checkbox" id="flexSwitchCheckChecked" @change="setCompleted($event, todo.id)" :checked="todo.completed">
    

    and then in the setCompleted handler do the below change

    setCompleted(event, id) {
      this.$store.commit('todos/setCompleted', {completed: event.target.checked , id: id})
    },
    

    and in your todoModules.js file do the below change in the setCompleted mutation

    setCompleted(state, payload) {
      const index = state.todos.findIndex(todo => todo.id === payload.id);
      state.todos[index].completed = payload.completed;
    }