97 votes

VueJS Comment puis-je utiliser une propriété calculée avec v-for ?

Comment puis-je utiliser la propriété calculée dans les listes. J'utilise VueJS v2.0.2.

Voici le HTML :

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

Voici le code de Vue :

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;
        },
    },
});

96voto

Bill Criswell Points 6231

Vous ne pouvez pas créer une propriété calculée pour chaque itération. Idéalement, chacune de ces items serait leur propre afin que chacun puisse avoir sa propre fullName propriété calculée.

Ce que vous pouvez faire, si vous ne voulez pas créer une user est d'utiliser une méthode à la place. Vous pouvez déplacer fullName dès le computed à la propriété methods alors vous pouvez l'utiliser comme suit :

{{ fullName(user) }}

Par ailleurs, si vous avez besoin de passer un argument à une fonction computed vous voulez probablement une méthode à la place.

1 votes

Et si vous avez besoin d'utiliser fullName plus d'une fois et que la méthode fullName est une fonction complexe qui serait vraiment coûteuse à faire 10 fois pour chaque itération ? Je pense que la solution de @PanJunie serait bien meilleure dans cette situation.

0 votes

Honnêtement, je ferais ça beaucoup plus haut dans l'État. L'approche calculée ci-dessous ne fonctionnerait pas si vous travaillez sur une version triée des articles. Je cherchais vraiment la sécurité ici. Il est beaucoup plus logique pour moi de rendre les données aussi réutilisables que possible beaucoup plus haut.

0 votes

Le problème, c'est que ce tableau peut changer en fonction des autres éléments qui sont sélectionnés et désélectionnés, ce qui déclenche la réactivité du tableau lui-même. Je ne peux donc pas vraiment le faire à un niveau supérieur, je ne pense pas, à part peut-être créer un filtre et ajouter des propriétés supplémentaires aux objets du tableau à ce moment-là (ce qui n'est pas une idée terrible mais entraîne une boucle supplémentaire dans le tableau pour faire tout cela - deux fois vaut mieux que dix fois, je suppose).

49voto

PanJunjie潘俊杰 Points 2063

Ce que vous ratez ici, c'est que votre items est un tableau, qui contient tous les éléments, mais l'élément computed est un seul fullName qui ne peut pas exprimer toutes les fullName en items . Essayez ceci :

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
    }
});

Puis dans la vue :

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

La façon dont le projet de loi est présenté fonctionne sûrement, mais nous puede le faire avec des accessoires calculés et je pense que c'est une meilleure conception que method en itérations, surtout quand l'application devient plus grande. Aussi, computed a un gain de performance par rapport à method dans certaines circonstances : http://vuejs.org/guide/computed.html#Computed-Caching-vs-Methods

0 votes

Je l'utilise pour mettre en surbrillance une ligne dans un fichier de type :class fonctionne mieux qu'une méthode.

4 votes

C'est moins portable. Si vous vouliez parcourir une copie triée de items et obtenir le nom complet, cela échouerait. Ce qui devrait vraiment être fait est un composant "utilisateur", qui prend un objet utilisateur, qui a une propriété calculée fullName.

0 votes

Pourquoi échouera-t-il quand items sont triés ? @BillCriswell

10voto

agm1984 Points 3539

J'aime la solution proposée par PanJunjie. Elle s'attaque au cœur du problème en itérant uniquement sur this.items une fois (pendant la phase de création du composant), puis de mettre le résultat en cache. C'est bien sûr le principal avantage de l'utilisation d'une prop calculée au lieu d'une méthode.

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>

La seule chose que je n'aime pas est que, dans le DOM markup, fullNames est accessible par l'index de la liste. Nous pourrions améliorer cela en utilisant .reduce() au lieu de .map() dans la prop calculée pour créer un objet avec une clé pour chaque item.id en this.items .

Prenons cet exemple :

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>

Remarque : l'exemple ci-dessus suppose que item.id est unique, comme dans le cas d'une sortie de base de données typique.

0voto

Gabor Simon Points 46

Peut-être ajouter un autre v-for qui itère à travers une liste d'un seul élément :

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

Ce n'est pas très joli, mais c'est ce que vous recherchez : un objet autour de cette portée qui a une propriété appelée fullName contenant cette valeur spécifique.

Et ce n'est pas seulement une caractéristique de vanité, parce que nous pouvons avoir besoin d'utiliser cette valeur à plus d'un endroit, par ex :

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

Mon cas d'utilisation était que je construisais des dates dans des boucles v-for (oui, un autre calendrier), comme :

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

(Par souci de brièveté, j'ai omis le :key="whatever" paramètres)

J'admets que la meilleure solution serait de déplacer ce composant vers un autre, mais si nous créons un nouveau composant pour chaque double ligne de ce type, et que nous n'utilisons ce composant qu'à cet endroit unique, nous polluons un autre espace de noms.

Peut-être un v-let="day as new Date(...)" serait utile à cet effet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X