Select2のtags機能を使った時に、既存の選択じゃなく新しく追加した時だけ実行する処理追加の仕方

# 更新

v.3.5.2を利用時の場合の内容を追記しました。~~前のバージョンは何だったんでしょうね？~~

# 背景

jQueryのセレクトボックスを拡張するライブラリ[Select2](https://select2.github.io/)が便利でちょこちょこと利用させて頂いているのですが、その中の[tagsの機能](https://select2.github.io/examples.html#tags)を使ってちょっとしたことを実装した時に、新しい要素が追加された時のイベントが用意されていないことに気づきました。

例えば、tagを追加した時にそのタイミングで非同期的にDBに登録する、とかができません。

---

# Version 3.5.2の場合

（今はもうv4出てますが…）

## 実装

オプションの内容が大分変わっており、`createTag`は`createSearchChoice`に、`select2:select`は`select2-selecting`に変更されています。

```js
$('.target').select2({
    tags: true,
    createSearchChoice: function(str) {
        // ここでreturnを実行しないと入力できなくなるので注意
        return {
            id: str,
            text: str,
            // この値があるかどうかで判断します
            isNewFlag: true
        };
    }
}).on('select2-selecting', function(e) {
    // 選択した要素に"isNewFlag"があるか？
    if(e.choice.isNewFlag === true) {
        // 新しく追加した要素だけ実行する処理
    }
});
```

## 利用例

optionタグが無くなっていますので、追加したタグの再利用周りの処理も変更しました。結構無理矢理な処理なので、これが最適なのかどうか…。

```js
// タグの選択リストは変数として用意しておく
var tags = [
    {
        id: 1,
        text: 'foo',
    }, {
        id: 2,
        text: 'bar',
    }
];

$('.target').select2({
    tags: function() {
        return tags;
    },
    createSearchChoice: function(str) {
        // ここでreturnを実行しないと入力できなくなるので注意
        return {
            id: str,
            text: str,
            // この値があるかどうかで判断します
            isNewFlag: true
        };
    }
}).on('select2-selecting', function(e) {
    var $select = $(this);

    // タグ追加前の値を保持
    var prevValStr = $select.val();
    var prevVal = [];
    if(prevValStr.length > 0) {
        // カンマ区切りでデータが来るので配列にしておく
        prevVal = prevVal.split(',');
    }

    // 選択した要素に"isNewFlag"があるか？
    if(e.choice.isNewFlag === true) {

        // "path/to/post"にPOSTする
        $.ajax({
            type: 'post',
            url: 'path/to/add',
            data: { value: e.choice.id },
            dataType: 'json'
        }).done(function(json) {
            // {id: number, text: string}
            // という形式で返ってくると仮定

            // タグ一覧に挿入することで使い回し出来るようにする
            tags.push({
                id: json.id,
                text: json.text
            });

            // タグ追加前の値に追加後の値を挿入して反映
            prevVal.push(json.id);
            $select.select2('val', prevVal);
        }).fail(function(data) {
            alert('何かミスってるっぽいよ');
        });

    }
});
```

---

# 旧バージョンの場合

（この当時どのバージョンを利用していたのか不明です…）

## 実装

tagsをtrueにしている状態でタイピングしてる時に実行される`createTag`というイベントがあるようなので、コチラを利用させてもらうことで実装できました。

```js
$('.target').select2( {
    tags: true,
    createTag: function( obj ) {
        // ここでreturnを実行しないと入力できなくなるので注意
        return {
            id:   obj.term,
            text: obj.term,
            // この値があるかどうかで判断します
            isNewFlag: true
        };
    }
} ).on( 'select2:select', function( e ) {
    // 選択した要素に"isNewFlag"があるか？
    if( e.params.data.isNewFlag ) {
        // 新しく追加した要素だけ実行する処理
    }
} );
```

## 利用例

背景で書いたような、新しく要素が追加された時にPOSTしてDBに保存する様な処理。

```js
$('.target').select2( {
    tags: true,
    createTag: function( obj ) {
        return {
            id:   obj.term,
            text: obj.term,
            isNewFlag: true
        };
    }
} ).on( 'select2:select', function( e ) {
    if( e.params.data.isNewFlag ) {
        var $select = $(this);

        // "path/to/post"にPOSTする
        $.ajax( {
            type: 'post',
            url: 'path/to/add',
            data: { value: e.params.data.id },
            dataType: 'json'
        } ).done( function( json ) {
            // optionタグを追加しておくことで消しても残るようになる
            $select.find( '[value="'+e.params.data.id+'"]' )
                .replaceWith( '<option selected value="'+json.id+'">' + e.params.data.text + '</option>' );
        } ).fail( function( data ) {
            alert( '何かミスってるっぽいよ' );
        } );
    }
} );
```

# 感想

`select2:***`とか、`select2-***`で用意してくれないかなぁ…。