Reputation: 197
I'm playing around with vue.js and react, and I'm currently trying to adapt a simple editable HTML table example from a book on React that I have in order to learn Vue.
Here is what happens in the code:
The problem I am having is with step 7. I can click multiple cells in my table and all of the cells will still have the contenteditable="true" set in them. If you look in Chrome dev tools you will see that the contenteditable tag sticks to all of the cells that are clicked on.
I believe the problem is happening because of the nested for loops I have, but I am not sure how to properly add keys to them to get it to work. I've tried to add keys but I think that I am not doing it correctly.
JSFiddle: https://jsfiddle.net/o69yaq7L/
Here is the code:
"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>
EDIT***
The problem is with this section of the code:
<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>
There is two v-for loops, and a if-else statement. The problem is where the if statement makes the td element have a contenteditable=true attribute. Once the td element gets that attribute, the attribute never leaves, even after further re-renders the td will still have the contenteditable=true set, even if that part of the if statement does not effect the element again. I think there is a problem with the way I am using id tags but I am not sure how to fix it.
Upvotes: 3
Views: 4702
Reputation: 73659
You have to give different keys to both if/else statements, like following:
<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>
See updated fiddle.
This is happening because Vue tries to render elements as efficiently as possible, often re-using them instead of rendering from scratch. Since in your case both if and else templace uses same element, is not replaced, just it's click event. As this isn’t always desirable though, so Vue offers a way for you to say, These two elements are completely separate - don’t re-use them, which can be done by adding key
attribute with unique values.
Upvotes: 5