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

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

Todo accessoire que je passe:

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

Mais il renvoie une erreur error Mutation inattendue de "todo" prop

2
Omama Zainab 1 sept. 2020 à 16:18

5 réponses

Meilleure réponse

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

Composant parent

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

Composant enfant

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

Méthode 2 - Transmettez les accessoires du composant parent sans modificateur de synchronisation et émettez un événement lorsque la valeur change. 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.

1
Anbuselvan Rocky 17 nov. 2020 à 11:40

L'un des principes de base de VueJS est que les composants enfants ne font jamais muter un prop.

Tous les accessoires forment une liaison one -way-down entre la propriété enfant et la propriété parent: lorsque la propriété parent est mise à jour, elle ira à l'enfant, mais pas l'inverse.

Si vous souhaitez mettre à jour le composant enfant todo.completed, vous avez deux choix:

Utiliser le modificateur .sync (recommandé)

Cette approche nécessitera quelques modifications de votre props. Vous pouvez en savoir plus ici.

Composant parent

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

Composant enfant

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

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

Composant enfant

<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>
1
Guru Prasad 1 sept. 2020 à 13:46

Qu'est-ce qui se passe ? : La mutation locale d'un accessoire est désormais considérée comme un anti-pattern, par ex. déclarant un accessoire et définissant ensuite this.myProp = 'someOtherValue' dans le composant. En raison du nouveau mécanisme de rendu, chaque fois que le composant parent effectue un nouveau rendu, les modifications locales du composant enfant seront écrasées.

Solution: vous pouvez le stocker en tant que données locales.

export default {
  name: "TodoItem",
  props: ["todo"],
  data() {
    return {
      todoLocal: this.todo,
    };
  },
  methods: {
    markComplete() {
      this.todoLocal.completed = !this.todoLocal.completed;
    },
  },
};
0
Sopheara Thoeun 20 déc. 2020 à 15:38

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>
2
Simeon Baleroso Anunciado 20 nov. 2020 à 00:29

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

1
dogversioning 1 sept. 2020 à 13:40