問題
在vue2中對(duì)表單控件有著良好的雙向數(shù)據(jù)綁定機(jī)制,但是對(duì)于要特定實(shí)現(xiàn)某些功能的輸入時(shí),我們就不得不使用到contenteditable="true"的div,而在這個(gè)div上是使用v-model是沒有效果的。那么問題就來了,輸入是非常需要雙向綁定的,這里的雙向數(shù)據(jù)綁定該如何實(shí)現(xiàn)?
當(dāng)然,說在這一段的前面,這種解決方式在vue2中是不行的,為什么這么說,因?yàn)楝F(xiàn)在去搜索這個(gè)問題絕大多數(shù)的搜索結(jié)果是這個(gè),所以放在前面。
實(shí)現(xiàn)的原理以及為什么不能用了
原理:自定義一個(gè)雙向數(shù)據(jù)綁定的指令,代碼如下:
Vue.directive('demo', {
twoWay:true,
bind:function(){
this.handler =function(){
this.set(this.el.innerHTML)
}.bind(this)
this.el.addEventListener('input',this.handler)
? ? },
update:function(newValue, oldValue){
this.el.innerHTML = newValue ||''
? ? },
unbind:function(){
this.el.removeEventListener('input',this.handler)
? ? }
})
至于this下的這些方法,在vue官網(wǎng)上可能不太容易找到,因?yàn)檫@些是vue1中的內(nèi)容,而在vue2中已經(jīng)被移除了。所以在vue2中我們是不能這么干的,當(dāng)然如果你使用的是vue1那么完全沒問題,直接拿去用即可。
單獨(dú)聲明一個(gè)組件,在組件內(nèi)部處理數(shù)據(jù)(也就是innerHTML),并將數(shù)據(jù)返回給父組件。
代碼如下:
v-html="innerText"
@input="changeText">
exportdefault{
props: ['value'],
? ? ? ? data(){
return{innerText:this.value}
? ? ? ? },
methods:{
? ? ? ? ? ? changeText(){
this.innerText =this.$el.innerHTML;
this.$emit('input',this.innerText);
? ? ? ? ? ? }
? ? ? ? }
? ? }
然后在父組件中直接使用v-model就可以了(這里我把組件名稱定義成了 v-edit-div)。
{{text}}
exportdefault{
? ? ? ? data(){
return{
text:'改一下試一試',
? ? ? ? ? ? }
? ? ? ? }
? ? }
至于為什么可以直接用v-model,看官網(wǎng)的 API 吧。
v-model 傳送門使用自定義事件的表單輸入組件,那一章節(jié)。
問題解決。
=============== 分割線:更新于17-08-25 =====================
忙的不行,之前在評(píng)論區(qū)也有發(fā)現(xiàn)這個(gè)例子其實(shí)會(huì)有不少的問題,包括如何實(shí)現(xiàn)異步數(shù)據(jù)的刷新,更新值之后光標(biāo)定位的問題等等,在考慮了異步數(shù)據(jù)和光標(biāo)問題后,有了以下的這個(gè)版本
<template>
v-html="innerText"
:contenteditable="canEdit"
@focus="isLocked = true"
@blur="isLocked = false"
@input="changeText">
? ? </div>
</template>
exportdefault{
name:'editDiv',
? ? ? ? props: {
? ? ? ? ? ? value: {
? ? ? ? ? ? ? ? type: String,
default:''
? ? ? ? ? ? },
? ? ? ? ? ? canEdit: {
type:Boolean,
default:true
? ? ? ? ? ? }
? ? ? ? },
data(){
return{
innerText:this.value,
isLocked:false
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? watch: {
'value'(){
if(!this.isLocked || !this.innerText) {
this.innerText =this.value;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? methods: {
? ? ? ? ? ? changeText(){
this.$emit('input',this.$el.innerHTML);
? ? ? ? ? ? }
? ? ? ? }
? ? }
</script>
? ? .edit-div {
width:100%;
height:100%;
? ? ? ? overflow: auto;
word-break:break-all;
? ? ? ? outline: none;
? ? ? ? user-select: text;
? ? ? ? white-space: pre-wrap;
? ? ? ? text-align: left;
&[contenteditable=true]{
? ? ? ? ? ? user-modify: read-write-plaintext-only;
? ? ? ? ? ? &:empty:before {
? ? ? ? ? ? ? ? content: attr(placeholder);
? ? ? ? ? ? ? ? display: block;
? ? ? ? ? ? ? ? color: #ccc;
? ? ? ? ? ? }
? ? ? ? }
? ? }
</style>
這個(gè)版本是在項(xiàng)目中最終使用的版本,需要用的直接拿走用即可。
注:
canEdit標(biāo)志這個(gè)div是否是可編輯的,在父組件直接使用v-model即可。
該組件應(yīng)該是一個(gè)div元素(也不一定非要是div)的子元素,父元素的大小即為子元素的大