问题:iview中表格组件通过render函数动态生成Input组件输入框插入表格中,具体UI样式如图,动态生成的Input输入框组件无法绑定v-model,这个时候我们需要实现双线绑定的功能。
解决:render函数生成Input框并且实现双向绑定的写法。
//这里只展示核心代码片段 render: (h, params)=>{ return h('Input', { props:{ value:params.row.curSum, autosize: true } on: { 'on-blur': (event) => { this.dataList.curSum=event.target.value; } } }) }
vue中有对render渲染函数的详细说明,这里不再描述,如果不慎了解可以阅读vue官方文档中的渲染函数章节:https://cn.vuejs.org/v2/guide/render-function.html。首先渲染Input组件时需要给value属性赋值,这个作用是父组件的value值改变,能够传递到子组件Input中。同时Input子组件中通过触发父组件的自定义函数on-blur将值传递给父组件(在ivew中Input子组件源码中通过this.$emit('on-blur', event)将值传入父组件),在父组件中触发on-blur事件后将值绑定在data对象上,这样就实现了双向绑定。
问题二:如上实现了双向绑定,但是引来了一个新的问题,当我们在一个输入框中输完值,点击第二个输入框进行输入时会触发前一个输入框的blur事件,之前我们为了实现双向绑定,会在输入框失焦时给data对象赋值,这个时候会引发整个一行输入框dom的重新绘制,从而使当前聚焦的input失焦,这个时候用户必须再点一次才能聚焦,这样非常影响用户体验。
解决方法:对于这个问题也进行了一段时间的探索,第一思路是官方有没有解决方案?,可惜查了很多资料也没有好的解决方案。第二思路想通过修改ivew的组件源码来解决这个问题,但是这样会导致之后iview UI库的版本管理会非常麻烦,可行性非常低。想了很久想到了第三种解决方案,通过hacker方法,能不能在用户点击Input输入框聚焦后,当数据绑定完成以及dom渲染完成以后在手动进行一次聚焦,也就是自动聚焦以后再手动触发一次聚焦。我们对上面的代码进行改进:
render: (h, params)=>{ return h('Input', { props:{ value:params.row.curSum, autosize: true }, attrs: { id: rwmc+'params.index'//保证每个输入框的id唯一 }, on: { 'on-focus': () => { this.$nextTick(()=>{ document.getElementById('rwmc'+params.index). childNodes.forEach(item=>{ if(item.tagName == 'INPUT'){ item.focus(); } }) }) }, 'on-blur': (event) => { this.dataList.curSum=event.target.value; } } }) }
这里,我们在之前的render函数中加了on-focus自定义事件。我们给每个Input定义一个id属性(注意保持id的唯一性)。当input触发父组件的on-focus事件时,我们在事件回调函数中调用vue的this.$nextTick方法,(这个方法是vue提供的一个api,它的作用是等dom渲染完成之后才执行的方法,具体$nextTick的作用可以参考vue官方文档https://cn.vuejs.org/v2/api/#Vue-nextTick ),由于前一个input框blur事件重新赋值后引起一整行dom重绘,在这里调用该方法是就是为了等到表格的dom都重新渲染完成后才触发之后的处理逻辑,在nextTick的回调函数中我们通过之前定义的id找到该dom下的input并手动聚焦。这样就完美的解决的之前数据框点击两次才聚焦的问题。
总结:个人用vue时间不长,但是感觉到vue非常的强大。vue官网定义vue是一套用于构建用户界面的渐进式框架。对于初学的前端和后端同学只需要了解基本的前端知识就可以快速的利用vue开发简单的前端应用,但是对于复杂的应用vue提供了丰富的api可成熟的解决方案,可以完美的应对复杂的应用。