Vueでインクリメンタルインプット

HTMLのinputタグで入力する際に、入力するごとに候補が絞られるインクリメンタルサーチのようなものを作ろうと思ったのですが、jQueryを使わないようにしたかったのと、Vueの勉強のため自作しました。
VueとBootstrap3.4.1を使っています。

サンプル含めたもろもろは以下にあります。

poruruba/VueIncrementalInput

https://github.com/poruruba/VueIncrementalInput

サンプルは以下で参照できます。

https://poruruba.github.io/VueIncrementalInput/index.html

Vueコンポーネントだけを見たい場合は以下で見れます。

https://poruruba.github.io/VueIncrementalInput/js/comp_history.js

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/5dde3a73-1b8b-08d5-a39a-2918083157cb.png)

#ソースコード

ソースコードを示しておきます。
Vueのコンポーネント化しています。

```js:js/comp_history.js
var comp_history = {
//  mixins: [mixins_bootstrap],
  props: ['value', 'list'],
  template: `
<div class="dropdown input-group">
  <input class="form-control" type="text" data-toggle="dropdown" v-model="input_text" v-on:input="input_change">
  <ul class="dropdown-menu">
    <li v-for="(item, index) in item_list" value="item" v-on:click="item_select(index)">
      <a style="padding-right: 0px;">{{item}} <span class="pull-right" v-on:click.stop="call_item_delete(index)">&nbsp;&nbsp;&nbsp; × &nbsp;&nbsp;</span></a>
    </li>
  </ul>
  <span class="input-group-addon" v-on:click="input_clear">×</span>
</div>
`,
  data: function(){
    return {
      input_text: this.value,
      all_list: [],
      item_list: [],
    }
  },
  methods: {
    call_list_add: function (list) {
      this.list_add(list);
      this.$emit('update:list', this.all_list);
      this.input_change();
    },
    call_item_add: function (item) {
      this.item_add(item);
      this.$emit('update:list', this.all_list);
      this.input_change();
    },
    call_list_clear: function () {
      this.all_list = [];
      this.$emit('update:list', this.all_list);
      this.input_change();
    },
    call_item_delete: function (index) {
      this.all_list.splice(index, 1);
      this.$emit('update:list', this.all_list);
      this.input_change();
    },

    list_add: function (list) {
      list.forEach(item => {
        this.item_add(item);
      });
    },
    input_clear: function () {
      this.input_text = "";
      this.input_change();
    },
    item_add: function (value) {
      var item = this.all_list.find(item => (item == value));
      if (!item)
        this.all_list.push(value);
    },
    item_select: function (index) {
      this.input_text = this.item_list[index];
      this.input_change();
    },
    input_change: function () {
      if (!this.input_text) {
        this.item_list = this.all_list;
      } else {
        var list = [];
        this.all_list.forEach(item =>{
          if (item.startsWith(this.input_text))
            list.push(item);
        });
        this.item_list = list;
      }

      this.$emit('input', this.input_text);
    },
  },
  mounted: function () {
    if (this.list)
      this.list_add(this.list);
    this.input_change();
  }
};

//export default comp_history;
```

Vueの双方向データバインディングを使っています。

#使い方

HTMLで以下のように指定します。

```html:index.html
<comp_history v-model="selected" v-bind:list.sync="list"></comp_history>
```

v-modelを指定すると、入力フォームに入力された値を親側がリアルタイムに取得・設定できます。
属性listに文字列の配列を指定すると、インクリメンタルサーチの候補を初期値設定できます。さらに、.syncを付けると、候補が追加されたり削除されたりしたときに親側に反映されます。
また、refを付けると、テンプレート参照でき、候補の追加削除などが親側からの関数呼び出しでできるようになります。（サンプルをご参照ください）
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/53cb3fa2-ac8e-f9a2-9c1a-b2a2d5030962.png)

#おわりに

以下もご参考まで。

　[Vueのカスタムコンポーネントで双方向データバインディングを入れてみた]( https://qiita.com/poruruba/items/db22fece6be12ad59365)

以上