可复制的简化示例:https://jsfiddle.net/nachocab/evc2374p/235/
更复杂的示例:https://jsfiddle.net/nachocab/evc2374p/125/(两次按向右箭头(不起作用),然后向左,然后向右(起作用)。More details
我知道这不是Vue方式,但是我需要修改从服务器接收到的HTML字符串(它表示带有幻灯片的幻灯片平台)。我遍历DOM树并将一些元素从data-hidden
更改为true
,我想触发幻灯片组件中的更新。
我已经尝试了几件事,包括更改组件键,发出事件和调用forceUpdate,但这是行不通的。
模板:
<div id="app">
<wrapper></wrapper>
</div>
<template id="wrapper">
<slide-deck>
<slide>
<p data-hidden="true">Hello</p>
</slide>
</slide-deck>
</template>
JS:
Vue.prototype.$eventBus = new Vue();
Vue.component('slide-deck', {
created() {
window.addEventListener('keydown', this.handleKeydown);
},
destroyed() {
window.removeEventListener('keydown', this.handleKeydown);
},
methods: {
handleKeydown(e) {
this.$slots.default[0].componentOptions.children[0].data.attrs['data-hidden']="false"
this.$eventBus.$emit('renderSlide')
},
},
render(h) {
console.log('render deck')
return h('div',{}, this.$slots.default)
}
})
Vue.component('slide', {
created() {
this.$eventBus.$on('renderSlide', () => {
this.$forceUpdate()
})
},
render(h) {
console.log('render slide', this.$slots.default[0].data.attrs['data-hidden'])
return h('div',{}, this.$slots.default)
}
})
Vue.component('wrapper', {
template: '#wrapper',
});
new Vue({
el: '#app'
});
答案 0 :(得分:2)
我认为这是因为您在子节点上更新了错误的属性。
您正在使用
child.data.attrs['data-hidden']="false"
但是您应该使用以下方法直接操作dom元素:
child.elm.dataset['hidden'] = false
话说回来,您还有其他问题。您将updateVisibilities
方法触发到尚未创建DOM的created
挂钩中。我建议您改用mounted
钩子。
最后,由于在数据更新后触发了updateVisibilities
方法,因此您还应该等待该方法,以便使用Vue nextTick
帮助器来更新DOM:
updateVisibilities() {
this.$nextTick(() => {
const validElements = this.currentSlide.componentOptions.children.filter(child => child.tag)
validElements.forEach(child => {
console.log(child, child.elm)
child.elm.dataset['hidden'] = child.elm.dataset['order'] > this.currentFragmentIndex
console.log('data-order', child.elm.dataset['order'], 'data-hidden', child.elm.dataset['hidden'])
})
})
}
在您的情况下,您也不需要任何$forceUpdate
。
这是您固定的两个小提琴:
我希望这会有所帮助!
编辑:这是完整的更新代码:
Vue.component('slide-deck', {
data() {
return {
currentSlideIndex: 0,
currentFragmentIndex: 0,
numFragmentsPerSlide: [2,2]
}
},
computed: {
slideComponents() {
return this.$slots.default.filter(slot => slot.tag)
},
currentSlide() {
return this.slideComponents[this.currentSlideIndex]
}
},
mounted () {
window.addEventListener('keydown', this.handleKeydown);
this.updateVisibilities();
},
destroyed() {
window.removeEventListener('keydown', this.handleKeydown);
},
methods: {
handleKeydown(e) {
if (e.code === 'ArrowRight') {
this.increaseFragmentOrSlide()
} else if (e.code === 'ArrowLeft') {
this.decreaseFragmentOrSlide()
}
},
increaseFragmentOrSlide() {
if (this.currentFragmentIndex < this.numFragmentsPerSlide[this.currentSlideIndex] - 1) {
this.currentFragmentIndex +=1
console.log('increased fragment index:', this.currentFragmentIndex)
} else if (this.currentSlideIndex < this.numFragmentsPerSlide.length - 1){
this.currentSlideIndex += 1
this.currentFragmentIndex = 0
console.log('increased slide index:', this.currentSlideIndex)
}
this.updateVisibilities()
},
decreaseFragmentOrSlide() {
if (this.currentFragmentIndex > 0) {
this.currentFragmentIndex -=1
console.log('decreased fragment:', this.currentFragmentIndex)
} else if (this.currentSlideIndex > 0) {
this.currentSlideIndex -= 1
this.currentFragmentIndex = 0
console.log('decreased slide:', this.currentSlideIndex)
}
this.updateVisibilities()
},
updateVisibilities() {
// this.$forceUpdate() // hack
this.$nextTick(() => {
const validElements = this.currentSlide.componentOptions.children.filter(child => child.tag)
validElements.forEach(child => {
console.log(child, child.elm)
child.elm.dataset['hidden'] = child.elm.dataset['order'] > this.currentFragmentIndex
console.log('data-order', child.elm.dataset['order'], 'data-hidden', child.elm.dataset['hidden'])
})
})
}
},
render(h) {
return h('div',{}, [this.currentSlide])
}
})
Vue.component('slide', {
render(h) {
return h('div',{}, this.$slots.default)
}
})
Vue.component('wrapper', {
template: '#wrapper',
});
new Vue({
el: '#app'
});