首页 > 解决方案 > VueJs 数组替换 v-for 如何使其工作

问题描述

我有一个非常简单的页面,其中包含两个选项卡,每个选项卡都包含一个具有不同状态的报告列表。

我尝试做的方式是,我有两个不同的数据源数组,一个用于每个选项卡,第三个数组表示当前显示的数组项。

当用户单击选项卡时,我根据单击的选项卡将第三个数组替换为第一个或第二个数组。

但是,该v-for部分未更新,但该v-show部分已更新。我花了好几个小时试图解决它,尝试过Vue.set(...)但仍然没有工作。

任何人都知道问题的详细原因以及最好的解决方案是什么?

以下是代码,我使用OnSenUI for Vueand 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);
  }

标签: javascriptvue.jsv-for

解决方案


来自Vue 文档中的列表渲染

当 Vue 更新使用 渲染的元素列表时v-for,默认情况下它使用“就地补丁”策略。...

这种默认模式是高效的,但仅适用于您的列表渲染输出不依赖于子组件状态或临时 DOM 状态(例如表单输入值)的情况。

为了给 Vue 一个提示以便它可以跟踪每个节点的身份,从而重用和重新排序现有元素,您需要key为每个项目提供一个唯一的属性。的理想值key是每个项目的唯一 ID。这个特殊的属性大致相当于track-by1.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;
  },
}

推荐阅读