VueJS Wie kann ich berechnete Eigenschaften mit v-for verwenden?

78

Wie kann ich berechnete Eigenschaften in Listen verwenden? Ich verwende VueJS v2.0.2.

Hier ist der HTML-Code:

<div id="el">
    <p v-for="item in items">
        <span>{{fullName}}</span>
    </p>
</div>

Hier ist der Vue-Code:

var items = [
    { id:1, firstname:'John', lastname: 'Doe' },
    { id:2, firstname:'Martin', lastname: 'Bust' }
];

var vm = new Vue({
    el: '#el',
    data: { items: items },
    computed: {
        fullName: function(item) {
            return item.firstname + ' ' + item.lastname;
        },
    },
});
Inoyatulloh
quelle

Antworten:

85

Sie können nicht für jede Iteration eine berechnete Eigenschaft erstellen. Im Idealfall itemswäre jede davon ihre eigene Komponente, sodass jede ihre eigene haben kannfullName berechnete Eigenschaft haben kann.

Wenn Sie keine userKomponente erstellen möchten, können Sie stattdessen eine Methode verwenden. Sie können fullNamedirekt von der computedEigenschaft zu wechseln methodsund sie dann wie folgt verwenden:

{{ fullName(user) }}

Nebenbei bemerkt, wenn Sie ein Argument an a übergeben müssen computed, möchten Sie wahrscheinlich stattdessen eine Methode.

Bill Criswell
quelle
Was ist, wenn Sie fullName mehrmals verwenden müssen und die fullName-Methode eine komplexe Funktion ist, deren Ausführung zehnmal für jede Iteration wirklich teuer wäre? Ich denke, @ PanJunies Lösung wäre in dieser Situation viel besser.
RichC
Ich würde das ehrlich gesagt viel weiter oben im Staat tun. Der unten berechnete Ansatz würde nicht funktionieren, wenn Sie an einer sortierten Version von Elementen arbeiten. Ich wollte hier definitiv in Sicherheit gehen. Für mich ist es viel sinnvoller, die Daten so weit wie möglich wiederverwendbar zu machen.
Bill Criswell
Mein Problem ist, dass sich das Array basierend auf anderen Elementen ändern kann, die ausgewählt und abgewählt werden, was die Reaktivität des Arrays selbst auslöst, sodass ich es nicht wirklich auf einer höheren Ebene tun kann, glaube ich, außer möglicherweise einen Filter zu erstellen und Hinzufügen zusätzlicher Eigenschaften zu den Objekten im Array an diesem Punkt (was keine schreckliche Idee ist, sondern nur eine Addition durch das Array bewirkt, um all das zu tun - zweimal ist besser als zehnmal, denke ich).
RichC
40

Was Sie hier vermissen, ist, dass Sie itemsein Array sind, das alle Elemente enthält, aber das computedist ein einzelnes Array fullName, das einfach nicht alle fullNames ausdrücken kann items. Versuche dies:

var vm = new Vue({
    el: '#el',
    data: { items: items },
    computed: {
        // corrections start
        fullNames: function() {
            return this.items.map(function(item) {
                return item.firstname + ' ' + item.lastname;
            });
        }
        // corrections end
    }
});

Dann in der Ansicht:

<div id="el">
    <p v-for="(item, index) in items">
        <span>{{fullNames[index]}}</span>
    </p>
</div>

Die Art und Weise, wie Bill sie vorstellt, funktioniert sicherlich, aber wir können es mit berechneten Requisiten tun, und ich denke, es ist ein besseres Design als methodbei Iterationen, insbesondere wenn die App größer wird. Hat computedauch einen Leistungsgewinn im Vergleich zu methodunter bestimmten Umständen: http://vuejs.org/guide/computed.html#Computed-Caching-vs-Methods

PanJunjie 潘俊杰
quelle
Ich benutze dies zum Hervorheben einer Zeile in einer :classDirektive funktioniert besser als eine Methode.
Hitautodestruct
3
Dies ist weniger tragbar. Wenn Sie eine sortierte Kopie von itemsdurchgehen und den vollständigen Namen erhalten möchten, schlägt dies fehl. Was wirklich getan werden sollte, ist eine "Benutzer" -Komponente, die ein Benutzerobjekt mit einer berechneten Eigenschaft "fullName" verwendet.
Bill Criswell
Warum schlägt es fehl, wenn itemssortiert werden? @ BillCriswell
PanJunjie 27
1
Wenn Sie zu tun , sind v-for="item in sortedVersionOfItems"die indexwäre weg.
Bill Criswell
3

Ich mag die Lösung von PanJunjie 潘俊杰. Es wird zum Kern des Problems, indem es nur this.itemseinmal (während der Komponentenerstellungsphase) wiederholt und dann das Ergebnis zwischengespeichert wird. Dies ist natürlich der Hauptvorteil für die Verwendung einer berechneten Requisite anstelle einer Methode.

computed: {
    // corrections start
    fullNames: function() {
        return this.items.map(function(item) {
            return item.firstname + ' ' + item.lastname;
        });
    }
    // corrections end
}

.

<p v-for="(item, index) in items">
    <span>{{fullNames[index]}}</span>
</p>

Das einzige, was mir nicht gefällt, ist, dass im DOM-Markup über den Listenindex darauf fullNameszugegriffen wird. Wir könnten dies verbessern, indem wir .reduce()anstelle .map()der berechneten Requisite ein Objekt mit einem Schlüssel für jedes item.idIn erstellen this.items.

Betrachten Sie dieses Beispiel:

computed: {
    fullNames() {
        return this.items.reduce((acc, item) => {
            acc[item.id] = `${item.firstname} ${item.lastname}`;
            return acc;
        }, {});
    },
},

.

<p v-for="item in items" :key="`person-${item.id}`">
    <span>{{ fullNames[item.id] }}</span>
</p>

Hinweis: Im obigen Beispiel wird davon item.idausgegangen, dass dies eindeutig ist, z. B. bei einer typischen Datenbankausgabe.

agm1984
quelle
0

Fügen Sie möglicherweise ein weiteres v-for hinzu, das eine Liste mit einem Element durchläuft:

<div id="el">
    <p v-for="item in items">
        <template v-for="fullName in [item.firstName + ' ' + item.lastName]">
            <span>{{fullName}}</span>
        </template>
    </p>
</div>

Nicht schön, aber genau das suchen Sie: ein Objekt in diesem Bereich mit einer Eigenschaft namens fullName, die diesen bestimmten Wert enthält.

Und es ist nicht nur eine Eitelkeitsfunktion, da wir diesen Wert möglicherweise an mehr als einer Stelle verwenden müssen, z.

<span v-if="...">I am {{fullName}}</span>
<span v-else-if="...">You are {{fullName}}</span>
<span v-else>Who is {{fullName}}?</span>

Mein Anwendungsfall war, dass ich Daten in v-for-Schleifen (ja, einem anderen Kalender) konstruierte, wie:

<v-row v-for="r in 5">
    <v-col v-for="c in 7">
        <template v-for="day in [new Date(start.getTime() + 24*60*60*1000*((c-1) + 7*(r-1)))]">
            <span>
                Some rendering of a day like {{day.getYear()}} and
                {{day.getMonth()}} etc.
            </span>
        </template>
    </v-col>
</v-row>

(Der Kürze halber habe ich das weggelassen :key="whatever" Einstellungen )

Ich gebe zu, dass der schönste Weg wäre, dies in eine separate Komponente zu verschieben, aber wenn wir für jeden solchen Zweiliner eine neue Komponente erstellen und diese Komponente nur an dieser einzigen Stelle verwenden, verschmutzen wir einfach einen anderen Namespace.

Vielleicht wäre eine v-let="day as new Date(...)"Richtlinie für diesen Zweck nützlich.

Gabor Simon
quelle