javascript - VueJs 数组替换 v-for 如何使其工作
问题描述
我有一个非常简单的页面,其中包含两个选项卡,每个选项卡都包含一个具有不同状态的报告列表。
我尝试做的方式是,我有两个不同的数据源数组,一个用于每个选项卡,第三个数组表示当前显示的数组项。
当用户单击选项卡时,我根据单击的选项卡将第三个数组替换为第一个或第二个数组。
但是,该v-for
部分未更新,但该v-show
部分已更新。我花了好几个小时试图解决它,尝试过Vue.set(...)
但仍然没有工作。
任何人都知道问题的详细原因以及最好的解决方案是什么?
以下是代码,我使用OnSenUI for Vue
and Vue router
,这是完整的组件代码:
<template id="main-page">
<v-ons-page>
<v-ons-toolbar>
<div class="center">Reports</div>
</v-ons-toolbar>
<v-ons-bar>
<p style="text-align: right; margin-right: 20px">
<v-ons-button @click="$ons.notification.alert('TODO: implement this.')">
+ New Report
</v-ons-button>
</p>
</v-ons-bar>
<v-ons-card v-for="report in showingReports" :key="report.requestNo">
<div class="title">
{{report.requestNo}}
</div>
<div class="content">
Submitted Date: {{report.submittedDate}}<br>
Status: {{report.status}}<br>
Subject: {{report.subject}}<br>
</div>
</v-ons-card>
<v-ons-card v-show="!showingReports || showingReports.length == 0">
<div class="content">
<p class="center">
No records found
</p>
</div>
</v-ons-card>
<v-ons-tabbar>
<v-ons-tab label="Open" @click="selectTab(0)" :active="selectedTab == 0"></v-ons-tab>
<v-ons-tab label="Closed" @click="selectTab(1)" :active="selectedTab == 1"></v-ons-tab>
</v-ons-tabbar>
</v-ons-page>
</template>
<script>
export default {
data() {
return {
showingReports: [],
openedReports: [],
closedReports: [],
selectedTab: 0
}
},
created() {
this.getReports();
},
methods: {
getReports() {
this.allReports = [
{
requestNo: "REPORT18070102",
submittedDate: Date(),
status: "OPENED",
subject: "Test1"
},
{
requestNo: "REPORT18070103",
submittedDate: Date(),
status: "OPENED",
subject: "Test2"
}
];
this.openedReports = this.allReports.filter(report => report.status == "OPENED");
this.closedReports = this.allReports.filter(report => report.status != "OPENED");
this.selectTab(0);
},
selectTab(tab) {
this.selectedTab = tab;
if(tab == 0){
this.showingReports = this.openedReports;
}else{
this.showingReports = this.closedReports;
}
},
}
}
为了包含更多细节,这是我通过阅读一些教程开始的一个简单的启动项目。这是 main.js:
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
这是 App.vue:
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
这是路由器文件夹下的 index.js。ReportList 是组件文件,位于 components 文件夹下:
import Router from 'vue-router'
import ReportList from '@/components/ReportList'
import 'onsenui/css/onsenui.css';
import 'onsenui/css/onsen-css-components.css';
import Vue from 'vue';
import VueOnsen from 'vue-onsenui';
Vue.use(Router)
Vue.use(VueOnsen)
export default new Router({
routes: [
{
path: '/',
name: 'ReportList',
component: ReportList
}
]
})
package.json 中的依赖:
"scripts": {
"prod": "webpack-dev-server --inline --progress --config build/webpack.prod.conf.js",
"start": "npm run prod",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
"build": "node build/build.js"
},
"dependencies": {
"onsenui": "^2.10.4",
"vue": "^2.5.2",
"vue-onsenui": "^2.6.1",
"vue-router": "^3.0.1"
},
-----删除了控制台日志,它没有帮助---
更新:我通过另一种方式使其工作,渲染整个列表但使用 v-show 来控制是否显示它。我认为与 v-show 相比,“v-for”不是很被动。
<v-ons-card v-for="report in allReports" :key="report.requestNo" v-show="shouldShow(report)">
<div class="title">
{{report.requestNo}}
</div>
<div class="content">
Submitted Date: {{report.submittedDate}}<br>
Status: {{report.status}}<br>
Subject: {{report.subject}}<br>
</div>
</v-ons-card>
shouldShow(report){
if(this.selectedTab == 0){
return report.status == 'OPENED';
}else{
return report.status != 'OPENED';
}
},
selectTab(tab) {
this.selectedTab = tab;
var hasItem = false;
this.allReports.forEach(r => hasItem = hasItem || this.shouldShow(r));
this.emptyList = !hasItem;
console.log(this.emptyList);
}
解决方案
来自Vue 文档中的列表渲染:
当 Vue 更新使用 渲染的元素列表时
v-for
,默认情况下它使用“就地补丁”策略。...这种默认模式是高效的,但仅适用于您的列表渲染输出不依赖于子组件状态或临时 DOM 状态(例如表单输入值)的情况。
为了给 Vue 一个提示以便它可以跟踪每个节点的身份,从而重用和重新排序现有元素,您需要
key
为每个项目提供一个唯一的属性。的理想值key
是每个项目的唯一 ID。这个特殊的属性大致相当于track-by
1.x 中的,但它的工作原理就像一个属性,所以你需要使用v-bind
它来将它绑定到动态值(这里使用简写)
因此,您应该始终为列表中的每个元素添加一个唯一key
属性(with v-bind
),以让 Vue 知道状态发生了变化。
例如,使用requestNo
每个列表项的属性:
<v-ons-card v-for="report in showingReports" :key="report.requestNo">
更新(继续我上面的评论):
您可能需要考虑使用 Vue 的计算属性。它们非常强大,并且在更新它们的依赖项时可以智能地更新属性。这也可能会解决您当前的列表未呈现的问题。
例如:
HTML
<v-ons-card v-for="report in computedReports" :key="report.requestNo">
<!-- ... -->
</v-ons-card>
JS
data: {
selectedTab: 0
},
computed: {
computedReports() {
return this.allReports.filter(report => this.isOpenedReports ?report.status != "OPENED" : report.status == "OPENED");
}
}
methods: {
getReports() {
this.allReports = [
{
requestNo: "REPORT18070102",
submittedDate: Date(),
status: "OPENED",
subject: "Test1"
},
{
requestNo: "REPORT18070103",
submittedDate: Date(),
status: "OPENED",
subject: "Test2"
}
];
this.selectTab(0);
},
selectTab(tab) {
this.selectedTab = tab;
},
}
推荐阅读
- java - 如何使用传入的泛型对象列表并使用我的 Java 方法返回泛型对象?
- joomla - 如何在 Virtuemart 中添加国家名称的翻译?
- ios - 调整导航返回按钮 iOS 13
- applescript - 如何获取应用程序打开窗口的文件路径?
- asp.net-core - 在 ubuntu 和 .net core 3.0 上缺少“系统”参考
- javascript - 按时间戳查询firestore中的数据
- vb.net - 多索引搜索
- typescript - 尝试登录或创建帐户时出现错误
- html - 将鼠标悬停在第二个下拉子菜单上时不会显示
- c# - Apache Ignite.NET 中的 IgniteQueue