簡介
TagsInput
是一種可編輯的輸入框,通過回車或者分號來分割每個標簽,用回退鍵刪除上一個標簽。用 vue
來實現還是比較簡單的。
先看效果圖,下面會一步一步實現他。
- 注:以下代碼需要vue-cli環境才能執行
(一)偽造一個輸入框
因為單行的文本框只能展示純文本,所以圖里面的標簽實際上都是 html元素
,用vue模板來寫的話,是這樣的:
<template>
<div class="muli-tags" @click='focus'>
<button class='btn' v-for='(tag, index) in tags' :key='index'>
{{tag}}
</button>
<input type="text" ref='input' v-model='current'>
</div>
</template>
<script>
export default {
name: 'TagsInput',
methods: {
focus () {
this.$refs.input.focus()
},
},
data () {
return {
tags: [],
current: ''
}
}
}
</script>
<style lang='less'>
.muli-tags{
padding: 5px 10px;
display: block;
border: 1px solid #ccc;
input{
background: transparent;
}
}
.btn{
margin: 0 5px 3px 0;
padding: 4px 5px;
background: #fff;
border: 1px solid #eee;
box-shadow: 0 0 4px;
}
</style>
(二)監聽輸入
在偽造好一個輸入框之后,我們對輸入框的事件進行處理,
- 回車和逗號會把input的值添加到tags數組,然后清空input
- 添加值之前,判斷tags數組是否已經包含同名的值
- 按回退鍵,刪除最近的一個標簽
// @keydown.188 188代表是是分號鍵的keyCode
<input type="text"
ref='input'
@keyup.enter="add"
@keydown.delete="del"
@keydown.188='split'
v-model='current'>
methods: {
// 按下分號鍵的時候,需要阻止默認事件,否則會出現分號
split (e) {
e.preventDefault()
this.add(e)
},
add (e) {
const val = e.target.value
if (!val) return
// 如果已經存在相同tag,不再添加
if (this.tags.indexOf(val) > -1) return
// 把輸入值添加到tag,並清空文本框
this.tags.push(val)
this.current = ''
},
del (e) {
// 當文本框內沒有值,再按回退鍵,則刪除最后一個tag
if (!e.target.value.length) {
this.tags.pop()
}
},
}
(三)刪除標簽
前面都是通過鍵盤來操作標簽,鼠標點擊標簽應該也是可以刪除的
<button class='btn' v-for='(tag, index) in tags' :key='index' @click='delTag(index)'>{{tag}} <span>x</span></button>
methods: {
// 刪除點擊的標簽
delTag (index) {
this.tags.splice(index, 1)
}
}
(四)自定義 v-model
通過上面的步驟,一個 tagsinput
組件就已經做好了,再給他添加自定義的 v-model
,讓他可以像input一樣響應表單數據。
// props
props: {
value: Array,
required: true,
default: () => []
}
// computed
computed: {
tags () {
return this.value.slice()
}
}
// methods
methods: {
// 刪除點擊的標簽
delTag (index) {
this.tags.splice(index, 1)
this.$emit('input', this.tags)
}
}
(五)完整代碼
// TagsInput.vue
<template>
<div class="muli-tags" @click='focus'>
<button class='btn' v-for='(tag, index) in tags' :key='index' @click='delTag(index)'>{{tag}} <span>x</span></button>
<input type="text"
ref='input'
@keyup.enter="add"
@keydown.delete="del"
@keydown.188='split'
v-model='current'>
</div>
</template>
<script>
export default {
props: {
value: Array,
required: true,
default: () => []
},
methods: {
focus () {
this.$refs.input.focus()
},
split (e) {
e.preventDefault()
this.add(e)
},
add (e) {
const val = e.target.value
if (!val) return
if (this.tags.indexOf(val) > -1) return
this.tags.push(val)
this.$emit('input', this.tags)
this.current = ''
},
del (e) {
if (!e.target.value.length) {
this.tags.pop()
this.$emit('input', this.tags)
}
},
delTag (index) {
this.tags.splice(index, 1)
this.$emit('input', this.tags)
}
},
computed: {
tags () {
return this.value.slice()
}
},
data () {
return {
current: ''
}
}
}
</script>
<style lang='less'>
.muli-tags{
padding: 5px 10px;
display: block;
border: 1px solid #ccc;
input{
background: transparent;
}
.btn{
margin: 0 5px 3px 0;
padding: 4px 5px;
background: #fff;
border: 1px solid #eee;
box-shadow: 0 0 4px;
}
}
</style>
作為組件被調用,這樣就可以看到像文章開頭那幅圖一樣的組件了。
// 父組件
<template>
<tags-input v-model='tags'/>
</template>
<script>
import TagsInput from './TagsInput.vue'
export default {
components: {
TagsInput
},
data () {
return {
tags: ['tag1', 'tag2', 'tag3']
}
}
}
</script>