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:
- User clicks on td element
- coordinates of td element are stored in rowSel, colSel variables
- coordinates change causes Vue to re-render the view
- the td cell that the user clicked has the "contenteditable=true" attribute added, as well as focusout event listener added
- user can then change the data in the td cell, and click off the cell to stop editing (focusout triggered)
- when user clicks off the cell, the rowSel and colSel variables are reset which causes re-render
- vue re-renders the view, and because the coordinates are reset, Vue should remove the contenteditable and focusout from the td element in question
- The data array containing the table data will then be updated with the text that the user entered when the cell was editable (not implemented yet)
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.