Vue.js でインクリメンタルサーチ

# インクリメンタルサーチとは
インクリメンタルサーチ（英語: incremental search）は、アプリケーションにおける検索方法のひとつ。検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる

## 完成品
<img width="388" alt="スクリーンショット 2020-04-02 22.40.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517237/d0cb43bd-cc16-2214-4b32-4cf0b6f93530.png">

<img width="321" alt="スクリーンショット 2020-04-02 22.40.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517237/43d27462-f8a4-ef4f-97a8-00729dc7c123.png">


### HTML
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>インクリメンタルサーチ</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="app">
        <!-- v-model を指定する -->
        <input type="text" placeholder="検索" v-model="search">

        <!-- sort とソート順選択欄を v-modelで結ぶ -->
        <select v-model="sort">
            <option value="">ソート無し</option>
            <option value="asc">昇順</option>
            <option value="desc">降順</option>
        </select>
<!-- v-for を使ったリストをアニメーションさせるためのコンポーネント <transition-group>key 属性を付ける必要がある -->
        <transition-group tag="ul">
            <!-- <li v-for="item in list">  listプロパティの値をv-forで回す -->
            <!-- fileterdListを表示 -->
            <li v-for="item in sortedList" v-bind:key="item.id">
                {{ item.text }}
            </li>
        </transition-group>
    </div>

    <script src="main.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
```

### JS
```javascript

new Vue({
    el: "#app",
    data:{
        //入力フォームに入力された値を表すデータを定義
        search: '',
        //現在どの並び順でソートしているのかを表すデータ
        sort: '',
        //、次は検索対象のデータを定義
        list: [
            { id: 1, text: 'Python' },
            { id:2, text: 'Ruby'},
            { id:3, text: 'PHP'},
            { id:4, text: 'JavaScript'},
            { id:5, text: 'Java'},
            { id:6, text: 'Go'},
            { id:7, text: 'C'},
            { id:8, text: 'C#'},
            { id: 9, text: 'Rails' },
            { id:10, text: 'Django'},
            { id:11, text: 'MySQL'},
            { id:12, text: 'Vue.jst'},
            { id:13, text: 'react'},
            { id:14, text: 'Docker'},
            { id:15, text: 'unity'},
            { id:16, text: 'jQuery'}
        ]
    },
    computed: {
        //検索処理としてfileterdListを定義
        filteredList: function(){
//filter のコールバック関数内では引数に配列の各項目が渡され、返り値が true となった項目のみ出力
            return this.list.filter(function(item){
//ある文字列に他の文字列が含まれているかどうかを調べる時は indexOfを使用
//item.text 文字列に、this.search という文字列が含まれている時に、その文字列の場所を表す数値を返し、含まれていないときには -1 を返し
                return item.text.indexOf(this.search) > -1
            }, this)
        },

        //ソート後のデータを返す算出プロパティ sortedList
        sortedList: function(){
            var copy = this.filteredList.slice()

            if(this.sort === 'asc' ){
                //comparatorAsc メソッドをコールバックとして sort 関数に渡す
                return copy.sort(this.comparatorAsc)
            } else if(this.sort === 'desc') {
                //降順に並べ替えるためのコールバック関数は comparatorDesc
                return copy.sort(this.comparatorDesc)
            } else{
                return copy
            }
        }
    },
    methods:{
        comparatorAsc: function(itemA, itemB){
            if(itemA.text < itemB.text){
                return -1
            } else if(itemA > itemB.text){
                return 1
            } else{
                return 0
            }
        },

        comparatorDesc: function(){
            if(itemA.text > itemB.text){
                return -1
            } else if(itemA < itemB.text){
                return 1
            } else{
                return 0
            }
        },
    }

});
```

### CSS
```css

body{
    font-family: sans-serif;
}

input,
select{
    padding: 2px 8px;
    font-size: inherit;
    vertical-align: middle;
}

ul {
    position: relative;
    margin-top: 6px;
    padding: 0;
    width: 300px;
    list-style: none;
}

li{
    margin: 0;
    padding: 10px;
    border-bottom: 1px solid #ddd;
}

.v-move{
    transition: transform 300ms ease-out;
}

.v-enter-active {
    transition: 300ms;
}

.v-enter{
    opacity: 0;
}

.v-enter-to{
    opacity: 1;
}

.v-leave-active{
    transition: 300ms;
}

.v-leave{
    opacity: 1;
}

.v-leave-to{
    opacity: 0;
}

```