Наблюдайте, как изменение состояния Vuex не работает должным образом с v-моделью - PullRequest
1 голос
/ 19 марта 2020

Я создал состояние глобальной ошибки с помощью Vuex, это массив с объектами всех текущих ошибок.

const store = new Vuex.Store({
  state: {
    errors: []
  },

  getters: {
    getErrors: state => state.errors
  },

  mutations: {
    setError: (state, message) => {
      state.errors.push({ error: true, message });
    },
    removeError: (state, i) => {
      state.errors.splice(i, 1);
    }
  }
});

У меня есть компонент, который динамически показывает все ошибки с использованием состояния Vuex и что я пытаюсь сделать, это удалить все объекты, для которых свойство error установлено в false, состояние свойства error обрабатывается мутацией setError и свойством v-model внутри компонента.

I'm пытаясь сделать это, наблюдая за изменениями и удаляя нужные элементы из массива, но это не приводит к правильному удалению, когда свойство меняется на false, как мне этого добиться?

Вот живая демонстрация https://codesandbox.io/s/vue-template-h5hf7

<template>
  <div id="snackbar">
    <v-snackbar
      v-for="(error, index) in getErrors"
      :key="index"
      v-model="error.error"
      color="red"
      :right="true"
      :timeout="2000"
      :top="true"
    >
      {{ error.message }}
      <v-btn dark text @click="removeError(index)">Close</v-btn>
    </v-snackbar>
  </div>
</template>

<script>
import { mapGetters, mapMutations } from "vuex";

export default {
  name: "ErrorSnackbar",

  computed: mapGetters(["getErrors"]),

  methods: {
    ...mapMutations(["removeError"]),
    removeError(i) {
      this.$store.commit("removeError", i);
    }
  },

  watch: {
    getErrors: {
      handler(newErrors) {
        if (newErrors.length > 0) {
          newErrors.forEach((error, i) => {
            if (error.error === false) {
              newErrors.splice(i, 1);
            }
          });
        }
      },
    }
  }
};
</script>

1 Ответ

2 голосов
/ 19 марта 2020

Ваш наблюдатель будет реагировать только на мутации массива напрямую (например, добавление или удаление элемента из него). Чтобы наблюдать за изменениями элементов в массиве, вам также необходимо использовать глубокий наблюдатель.

Кроме того, всякий раз, когда вы зацикливаетесь на массиве и удаляете элементы из массива одновременно, вы должны выполнять итерацию в в обратном порядке, в противном случае вы пропустите некоторые элементы.

watch: {
  getErrors: {
    deep: true,
    handler(newErrors) {
      for (let i = newErrors.length - 1; i >= 0; i--) {
        if (!newErrors[i].error) {
          newErrors.splice(i, 1)
        }
      }
    }
  }
}

Обратите внимание, что это может вызвать еще один вызов обработчику, поскольку вы изменяете объект, который вы наблюдаете.


EDIT

Спасибо за codeandbox .

Эта проблема связана с <v-snackbar>, не обновляющим модель. Я не совсем уверен, как реализован <v-snackbar>, но кажется, что когда компонент используется повторно, его таймаут отменяется, и он не генерирует событие input. Некоторые компоненты повторно используются в результате одновременного добавления и удаления нескольких ошибок.

Вам необходимо правильно назначить каждому <v-snackbar> один и тот же объект error. Прямо сейчас у вас есть ключ по индексу в массиве, но это изменится, когда элементы будут удалены из массива. Таким образом, мы должны придумать свой собственный уникальный идентификатор для каждого объекта ошибки.

Вот выдержка из изменений кода, которые необходимо внести:

// Define this at file-level
let nextKey = 1

mutations: {
  setError: (state, message) => {
    state.errors.push({
      key: nextKey++,
      error: true,
      message,
    })
  }
}
<v-snackbar
  v-for="error in getErrors"
  :key="error.key"
>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...