Website-Suche

javascript - Wie kann man Vue-Komponenten beim Entfernen eines Listenelements aktualisieren?


Ich weiß, wie man ein Listenelement aus einer Vue-Instanz entfernt. Wenn Listenelemente jedoch an Vue-Komponenten übergeben werden, wie entfernt man ein Listenelement, während die Komponenten mit den Listendaten synchron bleiben?

Hier ist der Anwendungsfall. Betrachten Sie ein Online-Forum mit einem Markdown-Editor. Wir haben eine Vue-Instanz, deren Daten eine Liste gespeicherter Kommentare sind, die von einem Server abgerufen werden. Diese Kommentare sollen in Markdowns geschrieben sein.

Um Bearbeitungen und Vorschauen zu erleichtern, haben wir auch eine Liste von Komponenten. Jede Komponente enthält einen bearbeitbaren Eingabepuffer sowie einen Vorschauabschnitt. Der Inhalt des gespeicherten Kommentars in der Vue-Instanz wird verwendet, um den Eingabepuffer zu initialisieren und ihn zurückzusetzen, wenn ein Benutzer eine Bearbeitung abbricht. Die Vorschau ist eine Transformation des Inhalts des Eingabepuffers.

Unten ist eine Testimplementierung:

<template id="comment">
    <div>
        Component:
        <textarea v-model="input_buffer" v-if="editing"></textarea>
        {{ preview }}
        <button type="button" v-on:click="edit" v-if="!editing">edit</button>
        <button type="button" v-on:click="remove" v-if="!editing">remove</button>
        <button type="button" v-on:click="cancel" v-if="editing">cancel</button>
    </div>
</template>

<div id="app">
    <ol>
        <li v-for="(comment, index) in comments">
            <div>Instance: {{comment}}</div>
            <comment
                v-bind:comment="comment"
                v-bind:index="index"
                v-on:remove="remove">
            </comment>
        </li>
    </ol>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>

<script>
let comments = ['111', '222', '333']

Vue.component('comment', {
  template: '#comment',
  props: ['comment', 'index'],
  data: function() {
    return {
      input_buffer: '',
      editing: false,
    }
  },
  mounted: function() { this.cancel() },
  computed: {
    preview: function() {
      // This is supposed to be a transformation of the input buffer,
      // but for now, let's simply output the input buffer
      return this.input_buffer
    },
  },
  methods: {
    edit:   function() { this.editing = true },
    remove: function() { this.$emit('remove', this.index) },
    cancel: function() { this.input_buffer = this.comment; this.editing = false },
    //save: function() {},  // submit to server; not implemented yet
  },
})

let app = new Vue({
  el: '#app',
  data: { comments: comments },
  methods: {
    remove: function(index) { this.comments.splice(index, 1); app.$forceUpdate() },
  },
})
</script>

Das Problem ist, dass, wenn wir einen Kommentar entfernen, die Komponenten nicht entsprechend aktualisiert werden. Zum Beispiel haben wir in der obigen Implementierung 3 Kommentare. Wenn Sie Kommentar 2 entfernen, wird die Vorschau von Element 3 weiterhin den Inhalt von Element 2 anzeigen. Sie wird nur aktualisiert, wenn wir bearbeiten drücken, gefolgt von abbrechen.

Ich habe app.$forceUpdate() ausprobiert, aber das hat nicht geholfen.

Antwort #1

Sie müssen nur das Attribut key in der v-for-Schleife wie folgt hinzufügen:

<li v-for="(comment, index) in comments" :key="comment">

Siehe funktionierendes Fiddle: https://fiddle.jshell.net/mimani/zLrLvqke/

Vue versucht, das Rendering zu optimieren. Durch die Bereitstellung des Schlüsselattributs behandelt es diese als völlig unterschiedliche Elemente und rendert sie ordnungsgemäß neu.

Siehe die key-Dokumentation für weitere Informationen.

Antwort #2

versuche mit:

Vue.component('comment', {
  template:
`<div>
  {{ comment }}
  <button v-on:click="remove"> X </button>
</div>`,
  props: ['comment', 'index'],
  methods: {
    remove: function() {
      this.$emit('remove', this.index);
   }
  },
});

vm = new Vue({
  el: '#app',
  data: {
   comments: ['a','b','c','d','e']
  },
  methods: {
    remove: function(index) { 
		this.comments.splice(index, 1);
	},
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"></script>


<div id="app">
    <ol>
        <li v-for="(comment, index) in comments">
            <comment
                v-bind:comment="comment"
                v-bind:index="index"
                v-on:remove="remove">
            </comment>
        </li>
    </ol>
</div>