You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Vuex中通过复选框修改循环中对象的指定属性

问题:修改Vuex中todos数组指定对象的completed属性

我有一个包含所需状态的todosModule,todos数组中存储着对象。当我循环渲染这些对象时,需要在复选框状态改变时,修改循环中对应对象的completed属性(该属性为布尔类型)。


原代码文件

1. todosModule.js(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
}

2. 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>

3. 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>

解决方案

1. 修改todosModule.js中的setCompleted mutation

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

2. 修改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($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>

核心说明

原代码的问题在于setCompleted mutation直接给todos数组添加了completed属性,而非修改数组内指定对象的属性。修改后:

  • 子组件复选框触发事件时,同时传递事件对象和当前todo的id
  • 提交mutation时,传入包含id和completed状态的对象作为参数
  • 在mutation中,通过findIndex定位到对应id的todo,再修改其completed属性

内容的提问来源于stack exchange,提问作者bobrov_site

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:03:24