2
votes

erreur Mutation inattendue du prop "todo" dans vue.js (j'utilise vue3)

Je crée une application todo dans vue.js qui a un composant TodoItem

{
  id:1,
  task:'todo 1',
  completed:false
}

todo accessoire que je passe:

<template>
  <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{todo.task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["todo"],
  methods: {
    markCompleted() {
      this.todo.completed = true
    },
  },
};
</script>

mais il lance une erreur d' erreur Mutation inattendue de l'accessoire "todo"


2 commentaires

Vous ne devez pas désactiver this.todo , vous devez émettre un événement et le muter dans le composant parent.


si vous parlez de faire ce change="$emit(markCompleted)" cela ne fonctionne pas, il lance la même erreur


4 Réponses :


1
votes

Vous ne pouvez pas modifier un accessoire depuis l'intérieur d'un composant - ils sont destinés à être définis par le parent uniquement. C'est un chemin de communication unidirectionnel.

Vous pouvez essayer l'une des deux choses suivantes: soit déplacer votre logique de détection d'une tâche terminée vers le parent, soit introduire l'accessoire dans une nouvelle variable dans le hook de cycle de vie data () (cela ne se produira que lorsque le composant est chargé pour le la première fois, vous ne pourrez donc pas effectuer de mise à jour depuis l'extérieur du composant, si cela est important pour votre cas d'utilisation).


0 commentaires

1
votes

Méthode 1 (Vue 2.3.0+) - À partir de votre composant parent, vous pouvez passer prop avec le modificateur de synchronisation

Composant parent

    <template>
      <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
        <p>
          <input type="checkbox" @change="markCompleted" />
          {{todo.task}}
          <button class="del">x</button>
        </p>
      </div>
    </template>
    
    <script>
    export default {
      name: "TodoItem",
      props: ["todo_prop"],
      data() {
         return {
            todo: this.todo_prop
         }
      },
      methods: {
        markCompleted() {
          this.todo.completed = true
        },
      },
    };
    </script>

Composant enfant

<TodoItem v-for="todo in todoList" :key="todo.id" todo_prop.sync="todo">

Méthode 2 - Passez les accessoires du composant parent sans modificateur de synchronisation et émettez un événement lorsque la valeur a changé. Pour cette méthode, tout le reste est également similaire. Il suffit d'émettre un événement lorsque l'élément todo est devenu terminé.

Le code n'a pas été testé. Toutes mes excuses si quelque chose ne fonctionne pas.


0 commentaires

1
votes

L'un des principes fondamentaux de VueJS est que les composants enfants ne mutent jamais un prop .

Tous les accessoires forment un -way-down liaison entre la propriété des enfants et celui des parents: lorsque les mises à jour de propriété mère, il coulera à l'enfant, mais pas l'inverse.

Si vous souhaitez que la mise à jour du composant enfant soit todo.completed , vous avez deux choix:

Utilisez le modificateur .sync (recommandé)

Cette approche nécessitera un peu de modification de vos props . Vous pouvez en savoir plus ici .

Composant parent

<template>
  <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{todo.task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["todo"],
  methods: {
    markCompleted() {
      this.$emit('set-completed', true)
    },
  },
};
</script>

Composant enfant

<template>
<div>
  ...
  <todo-item :todo="nextTodo" @set-completed="$value => { nextTodo.completed = $value }/>
</div>
</template>

Utilisez un événement personnalisé

Vue vous permet de configurer des écouteurs dans votre parent pour les événements que l'enfant émettra. Votre composant enfant peut utiliser ce mécanisme pour demander au parent de changer les choses. En fait, le modificateur .sync ci-dessus fait exactement cela dans les coulisses.

Composant parent

<template>
  <div class="todo-item" v-bind:class="{'is-completed':completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["task", "completed"],
  methods: {
    markCompleted() {
      this.$emit('update:completed', true)
    },
  },
};
</script>

Composant enfant

<template>
<div>
  ...
  <todo-item :task="nextTodo.task" :completed.sync="nextTodo.completed"/>
</div>
</template>


0 commentaires

2
votes

pour moi, pour résoudre ce problème, je stocke les accessoires dans les données todos, je regarde les didacticiels brad vue et j'obtiens cette erreur, ce sont mes codes réels et leur fonctionnement
            <template>
        <div class="todo-item" v-bind:class="{'is-complete':todo.completed}">
            <p>
            <input type="checkbox" v-on:change="markComplete(todo.completed)" v-bind:checked="todo.completed">
            {{todo.title}} 
            <!-- <button @click="$emit('del-todo', todo.id)" class="del">x</button> -->
            </p>

        </div>
        </template>

        <script>
        export default {
        name: "TodoItem",
        props: ["todo"],
        data(){
            return{
                todos : this.todo
            }
        },
        methods: {
            markComplete(isComplete) {
                this.todos.completed =! isComplete
            }
        }
        }
        </script>

        <style scoped>
        .todo-item {
            background: #f4f4f4;
            padding: 10px;
            border-bottom: 1px #ccc dotted;
        }
        .is-complete {
            text-decoration: line-through;
        }
        .del {
            background: #ff0000;
            color: #fff;
            border: none;
            padding: 5px 9px;
            border-radius: 50%;
            cursor: pointer;
            float: right;
        }
        </style>


0 commentaires