【问题标题】:vue.js not updating attributes in nested v-for loopsvue.js 不更新嵌套 v-for 循环中的属性
【发布时间】:2016-12-07 12:00:25
【问题描述】:

我正在使用 vue.js 和 react,我目前正在尝试从一本关于 React 的书中改编一个简单的可编辑 HTML 表格示例,以便学习 Vue。

以下是代码中发生的情况:

  1. 用户点击 td 元素
  2. td 元素的坐标存储在 rowSel、colSel 变量中
  3. 坐标变化导致 Vue 重新渲染视图
  4. 用户点击的td单元格添加了“contenteditable=true”属性,并添加了focusout事件监听器
  5. 然后用户可以更改 td 单元格中的数据,并单击该单元格以停止编辑(触发聚焦)
  6. 当用户单击单元格时,rowSel 和 colSel 变量会被重置,从而导致重新渲染
  7. vue 重新渲染视图,并且由于坐标被重置,Vue 应该从有问题的 td 元素中删除 contenteditable 和 focusout
  8. 包含表格数据的数据数组随后将使用用户在单元格可编辑时输入的文本进行更新(尚未实现)

我遇到的问题是第 7 步。我可以单击表格中的多个单元格,并且所有单元格中仍会设置 contenteditable="true"。如果你查看 Chrome 开发工具,你会看到 contenteditable 标签贴在所有被点击的单元格上。

我相信问题的发生是因为我有嵌套的 for 循环,但我不确定如何正确地向它们添加键以使其工作。我尝试添加密钥,但我认为我做得不对。

JSFiddle: https://jsfiddle.net/o69yaq7L/

代码如下:

"use strict";

var headers = [
    "Book", "Author", "Language", "Published", "Sales"
];

var data = [
    ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954-1955", "150 million"],
    ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"],
    ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"],
    ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"],
    ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754-1791", "100 million"],
    ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"],
    ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"],
];

var Excel = new Vue({
	el: '#app',

	data: {
		colData: data,
		headers: headers,		
		colSel: -1,
		rowSel: -1
	},
	methods: {
		dataClick: function(ev){	
			this.colSel = ev.target.cellIndex;
			this.rowSel = ev.target.parentElement.rowIndex - 1;
			Vue.nextTick( function(){
				ev.target.focus();
			});
		},

		lostFocus: function(ev){						
			console.log(ev.target.textContent);
			//var changedRow = this.colData.slice(this.rowSel,this.rowSel+1);
			this.colSel = -1;
			this.rowSel = -1;
			console.log(this.colSel + " " + this.rowSel);
			
		}
	}
});
<html>
<head>
    <title>
        VUEJS Testing
    </title>
</head>
<body>
    <div id="app">        
        <table>
            <thead>
                <tr>
                    <th v-for="item in headers" :key="item.id">{{item}}</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(row,rowInd) in colData"  :key="row.id">
                	<template v-for="(item,colInd) in row"  >
                		<template v-if="rowInd == rowSel && colInd == colSel">
                    		<td contenteditable="true" v-on:focusout="lostFocus" :key="item.id">{{item}}</td>
                    	</template>
                    	<template v-else>
                    		<td v-on:dblclick="dataClick">{{item}}</td>
                    	</template>
                    </template>
                </tr>
            </tbody>
        </table>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.4/vue.min.js"></script>   
</body>
</html>

编辑***

问题出在这部分代码上:

<tr v-for="(row,rowInd) in colData" :key="row.id">
  <template v-for="(item,colInd) in row">
    <template v-if="rowInd == rowSel && colInd == colSel">
      <td contenteditable="true" v-on:focusout="lostFocus" :key="item.id">{{item}}</td>
    </template>
    <template v-else>
      <td v-on:dblclick="dataClick">{{item}}</td>
    </template>
  </template>
</tr>

有两个 v-for 循环和一个 if-else 语句。问题在于 if 语句使 td 元素具有 contenteditable=true 属性。一旦 td 元素获得该属性,该属性就永远不会离开,即使在进一步重新渲染之后, td 仍将设置 contenteditable=true ,即使 if 语句的该部分不再影响该元素。我认为我使用 id 标签的方式存在问题,但我不确定如何解决。

【问题讨论】:

  • 请更新 sn-p 以显示必须显示的值以进行调试,因为目前它只是表格,很难说发生了什么,因为 vue-devtools 无法在 jsfiddle 上运行

标签: javascript vue.js


【解决方案1】:

您必须为两个 if/else 语句提供不同的键,如下所示:

<tr v-for="(row,rowInd) in colData"  :key="row.id">
    <template v-for="(item,colInd) in row"  >
        <template v-if="rowInd == rowSel && colInd == colSel">
            <td contenteditable="true" v-on:focusout="lostFocus" :key="item.id"  key="key1">{{item}}</td>
        </template>
        <template v-else>
            <td v-on:dblclick="dataClick" key="key2">{{item}}</td>
        </template>
    </template>
</tr>

查看更新的fiddle

这是因为 Vue 试图尽可能高效地渲染元素,经常重复使用它们而不是从头开始渲染。由于在您的情况下 if 和 else 模板都使用相同的元素,因此不会被替换,只是单击事件。由于这并不总是可取的,因此 Vue 提供了一种方式来表示 These two elements are completely separate - don’t re-use them,这可以通过添加具有唯一值的 key 属性来完成。

【讨论】:

  • 你能告诉我“key”和“:key”(前面有冒号的key)有什么区别吗?我在 Vue 网站上看到了使用“:key”添加密钥的参考,例如:vuejs.org/v2/guide/list.html#key.
  • :key 等价于v-bind:key,用于将其绑定到动态值,即。 e.任何 vue 实例变量。
猜你喜欢
  • 1970-01-01
  • 2019-04-24
  • 1970-01-01
  • 2021-12-15
  • 2020-11-01
  • 2019-02-04
  • 2021-01-08
  • 2017-06-09
  • 2021-03-23
相关资源
最近更新 更多