VueJS Làm cách nào để sử dụng thuộc tính được tính toán với v-for


82

Làm cách nào để sử dụng thuộc tính được tính toán trong danh sách. Tôi đang sử dụng VueJS v2.0.2.

Đây là HTML:

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

Đây là mã 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;
        },
    },
});

Câu trả lời:


87

Bạn không thể tạo thuộc tính tính toán cho mỗi lần lặp. Tốt nhất, mỗi thành phần đó itemssẽ là thành phần riêng của chúng để mỗi thành phần có thể có thuộc fullNametính tính toán riêng .

Những gì bạn có thể làm, nếu bạn không muốn tạo một userthành phần, là sử dụng một phương pháp thay thế. Bạn có thể di chuyển fullNamengay từ thuộc computedtính sang methods, sau đó bạn có thể sử dụng nó như:

{{ fullName(user) }}

Ngoài ra, lưu ý thêm, nếu bạn thấy mình cần chuyển đối số đến computedmột phương thức, bạn có thể muốn một phương thức thay thế.


Điều gì sẽ xảy ra nếu bạn cần sử dụng fullName nhiều lần và phương thức fullName là một hàm phức tạp sẽ thực sự tốn kém để thực hiện 10 lần cho mỗi lần lặp? Tôi nghĩ rằng giải pháp của @ PanJunie sẽ tốt hơn nhiều trong tình huống đó.
RichC

Thành thật mà nói, tôi sẽ làm điều này cao hơn nhiều trong tiểu bang. Cách tiếp cận được tính toán bên dưới sẽ bị hỏng nếu bạn đang làm việc trên phiên bản được sắp xếp của các mục. Tôi chắc chắn sẽ an toàn ở đây. Đối với tôi, việc làm cho dữ liệu có thể tái sử dụng được càng nhiều càng tốt càng tốt.
Bill Criswell

Vấn đề của tôi là, mảng đó có thể thay đổi dựa trên các mục khác được chọn và bỏ chọn, điều này sẽ kích hoạt phản ứng của chính mảng đó, vì vậy tôi không thể thực sự làm điều đó ở cấp độ cao hơn, tôi không nghĩ, ngoài việc có thể tạo một bộ lọc và thêm các thuộc tính bổ sung vào các đối tượng trong mảng tại thời điểm đó (đó không phải là một ý tưởng khủng khiếp nhưng chỉ gây ra một vòng lặp cộng qua mảng để làm tất cả những điều đó - tôi đoán là hai lần tốt hơn 10 lần).
RichC

43

Điều bạn còn thiếu ở đây là itemsmảng của bạn là một mảng, chứa tất cả các mục, nhưng là một mảng computedduy nhất fullName, không thể thể hiện tất cả các fullNames trong items. Thử cái này:

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

Sau đó, trong chế độ xem:

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

Cách mà Bill giới thiệu chắc chắn hiệu quả, nhưng chúng ta có thể làm điều đó với các đạo cụ được tính toán và tôi nghĩ đó là một thiết kế tốt hơn so với methodlặp lại, đặc biệt là khi ứng dụng lớn hơn. Ngoài ra, computedcó hiệu suất tăng so với methodtrong một số trường hợp: http://vuejs.org/guide/computed.html#Computed-Caching-vs-Methods


Tôi đang sử dụng điều này để làm nổi bật một hàng trong một :classchỉ thị hoạt động tốt hơn một phương pháp.
hitautodestruct

3
Điều này là ít di động hơn. Nếu bạn muốn xem qua một bản sao được sắp xếp itemsvà lấy fullName, điều này sẽ không thành công. Điều thực sự nên làm là một thành phần "người dùng", lấy một đối tượng người dùng, có thuộc tính tính toán fullName.
Bill Criswell

Tại sao nó sẽ không thành công khi itemsđược sắp xếp? @BillCriswell
PanJunjie 潘俊杰

1
Nếu bạn đã làm v-for="item in sortedVersionOfItems"việc indexsẽ được tắt.
Bill Criswell

3

Tôi thích giải pháp được đăng bởi PanJunjie 潘俊杰. Nó là trọng tâm của vấn đề bằng cách chỉ lặp lại this.itemsmột lần (trong giai đoạn tạo thành phần), và sau đó lưu vào bộ nhớ đệm kết quả. Tất nhiên, đây là lợi ích chính của việc sử dụng một phương thức tính toán thay vì một phương pháp.

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>

Điều duy nhất tôi không thích là, trong đánh dấu DOM, fullNamesđược truy cập bởi chỉ mục danh sách. Chúng tôi có thể cải thiện điều đó bằng cách sử dụng .reduce()thay vì .map()trong đề xuất được tính toán để tạo một Đối tượng với một khóa cho mỗi item.idtrong this.items.

Hãy xem xét ví dụ này:

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>

Lưu ý: ví dụ trên giả định rằng đó item.idlà duy nhất, chẳng hạn như từ đầu ra cơ sở dữ liệu điển hình.


0

Có thể thêm một v-for khác lặp lại qua danh sách dài một mục:

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

Không đẹp, nhưng đó là những gì bạn đang tìm kiếm: một đối tượng xung quanh khoảng đó có thuộc tính gọi là fullName chứa giá trị cụ thể đó.

Và nó không chỉ là một tính năng hư ảo, vì chúng ta có thể cần sử dụng giá trị đó ở nhiều nơi, ví dụ:

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

Trường hợp sử dụng của tôi là tôi đang tạo ngày trong vòng lặp v-for (vâng, một lịch khác), như:

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

(Để ngắn gọn, tôi đã bỏ qua :key="whatever"cài đặt)

Tôi thừa nhận rằng cách tốt nhất sẽ là di chuyển nó sang một thành phần riêng biệt, nhưng nếu chúng ta tạo một thành phần mới cho mọi chữ lót như thế này và chỉ sử dụng thành phần đó tại nơi duy nhất này, thì chúng ta chỉ làm ô nhiễm một không gian tên khác.

Có thể một v-let="day as new Date(...)"chỉ thị sẽ hữu ích cho mục đích như vậy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.