Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue组件--TagsInput #7

Open
some-code opened this issue Jun 5, 2018 · 0 comments
Open

vue组件--TagsInput #7

some-code opened this issue Jun 5, 2018 · 0 comments
Labels

Comments

@some-code
Copy link
Owner

简介

TagsInput 是一种可编辑的输入框,通过回车或者分号来分割每个标签,用回退键删除上一个标签。用 vue 来实现还是比较简单的。

先看效果图,下面会一步一步实现他。
image

  • 注:以下代码需要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>
@some-code some-code added the vue label Jun 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant