問題 Q1
實作拖拉的元件,當拉(如下圖)名為name的theader物件,拉到number欄位
欄位名稱後面的數字代表該欄位在資料陣列的index
然後放開,照理說name欄位和number欄位應該要互換,但是...
可以看到theader欄位沒有發生變動,但tbody所應對的theader有正確渲染,然後欄位名稱後的數字也有正確變化,代表資料陣列是有正確改動的,但是畫面沒有正確渲染。
<table class="table table-bordered">
<thead>
<drag-wrap :list="headers" tag="tr" @watchData="watchData">
<drag-item v-for="(header, index) in headers" :key="index" tag="th">
{{ header }} , {{ index }}
</drag-item>
</drag-wrap>
</thead>
<tbody>
<tr v-for="(item, index) in items" :key="item.number">
<td v-for="(header, sindex) in headers" :key="sindex">
{{ item[header] }}
</td>
</tr>
</tbody>
</table>
data() {
return {
headers: ['number', 'name', 'sex', 'age', 'position'],
items: [
{
number: 35,
name: 'Durant',
sex: 'male',
age: 34,
position: 'SF'
},
{
number: 23,
name: 'James',
sex: 'male',
age: 37,
position: 'SF'
},
{
number: 30,
name: 'Curry',
sex: 'male',
age: 34,
position: 'PG'
},
]
}
},
解決 for Q1
<table class="table table-bordered">
<thead>
<drag-wrap :list="headers" tag="tr" @watchData="watchData">
<drag-item v-for="(header, index) in headers" :key="header" tag="th">
{{ header }} , {{ index }}
</drag-item>
</drag-wrap>
</thead>
<tbody>
<tr v-for="(item, index) in items" :key="item.number">
<td v-for="(header, sindex) in headers" :key="sindex">
{{ item[header] }}
</td>
</tr>
</tbody>
</table>
第四行v-for的key屬性由index改成header(陣列元素本身 string),詳細原因的原理還尚未弄明白,需要更知道key在v-for所代表的意義
https://cn.vuejs.org/v2/guide/list.html#维护状态
問題 Q2
跟Q1 類似,一樣是表格變動不如預期
解決 for Q2
先解釋程式flow :
在元件內部有用props接收外部data的資料(headers),每次移動完成後,會觸發ondragend事件,
該事件執行的function會先拿到打亂資料的index,再去跟props的原始header陣列度對比,拿到拖拉完成後的新headers陣列。
但這樣就會發生問題..
因為我發現每次拖拉完成後,都會重新render內部元件(drag-wrap元件),而重新render後,從父元件拿到的props資料header就不會是最原始的順序,而是每次拖拉前的header順序
所以..
在內部元件drag-wrap 的created階段先接收最原始的header,保存下來後,每次拖拉結束後,與打亂資料的index比對的headers就會是最原始的順序。
以下code:
created階段origin_list變數會接收props list(外部傳入參數是headers)
// drag-wrap.js
import {h, defineComponent} from 'vue'
const drapWrapComponent = defineComponent({
name: 'dragWrap',
props: {
list: {
type: Array
},
tag: {
type: String,
default: 'div'
}
},
emits: ['watchData'],
data() {
return {
fromDom: null,
toDom: null,
children: [],
origin_list: [],
}
},
render() {
try {
// ES6 解構賦值
const { $slots, tag } = this;
return h(
tag,
{
ref: 'wrap',
ondragenter: event => {
event.preventDefault()
},
ondragover: event => {
event.preventDefault()
}
},
$slots.default())
} catch (err) {
return h('pre', {style: {color: 'red'}}, err.stack)
}
},
created() {
this.emitter.on('dragstart', this.onDragstart)
this.emitter.on('dragenter', this.onDragenter)
this.emitter.on('dragend', this.onDragend)
this.emitter.on('putChild', child => {
this.children.push(child)
console.log(child)
})
this.origin_list = this.list
console.log(this.origin_list);
},
methods: {
onDragstart(el) {
this.fromDom = el
console.log('拿起' + this.fromDom.innerHTML)
},
onDragenter(el) {
this.toDom = el
if(this.toDom === this.fromDom) return
console.log('進入到' + this.toDom.innerHTML)
// 判斷進入node是否在起始node前面
if(this.isPrevNode(this.fromDom, this.toDom)) {
// 把參數一放在參數二前面
console.log('交換');
this.$refs.wrap.insertBefore(this.fromDom, this.toDom)
} else {
console.log('交換');
this.$refs.wrap.insertBefore(this.fromDom, this.toDom.nextSibling)
}
},
onDragend() {
// this.$emit('watchData', this.list)
console.log('執行onDragend')
if(!this.list.length) return
// this.$el.children是偽陣列
// 獲取drag-item的Dom
console.log(this.$el.children);
const realDomOrder = [...this.$el.children].filter(child =>
child.classList.contains('__drag_item')
)
this.getDataOrder(realDomOrder, this.children)
},
// to是否在from的前面
isPrevNode(from, to) {
while(from.previousSibling != null) {
if(from.previousSibling === to) {
return true
}
from = from.previousSibling
}
},
getDataOrder(realList, originList) {
// 拿到打亂item的index
console.log(realList);
console.log(originList);
const order = realList.map(realItem => {
return originList.findIndex(dragItem => realItem === dragItem)
})
console.log(order);
const newData = []
order.forEach((item, i) => {
newData[i] = this.origin_list[item]
})
console.log(newData);
this.$emit('watchData', newData)
}
},
})
export default drapWrapComponent
Top comments (2)
Be careful, working with the
index
of av-for
is to be avoid. It's actually counter-productive to do so, because it may change if you have some mutations on your array/object, resulting into an index shift while it should always stay the same as much as possible.thank for your advice!