Select2でタグの見た目をダイナミックに変更する

# はじめに
Select2の見た目をダイナミックに変更するのに多少ハマり、トリッキーな方法で解決したので共有したい。

# やりたいこと
「北海道」というタグが既にあったときに、「update_data」ボタンをクリックすると、下のように★をつけるなどしてタグの見た目をダイナミックに変更したい

### 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/3fceca70-9c75-4fdc-575e-a01239e21ad5.png)

### 変更後（ボタンクリック後）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/8794f9f7-6501-7cff-df8b-6b0bfdc017f0.png)

「プログラム的にタグを削除して編集したものを追加すればいいだけだろ」とタカをくくっていたがJavaScriptの同期処理の問題か、なかなかうまくいかず。

# 解決方法
## 1. templateSelection の設定
select2のtemplateSelectionを設定することで、タグに追加する際のタグ名に装飾を施すことができるので、この仕組みを使う。
select2オブジェクトを初期化する際に、ここに事前に装飾したい処理をいれておく。
今回は、元々のタグに、"prefix"というidの入力フィールドに設定された値を接頭語に加えて返却するという処理をいれてみた。入力フィールドに値を設定しなければ、装飾されないタグ名がそのまま表示されるだけとなる。

```javascript
templateSelection:function(param){
    return document.getElementById("prefix").value + param.text
}
```

## 2. タグの更新
templateSelectionは、最初にタグを登録する時や、後から別のタグを追加する際に呼び出される。それだけでなく既に追加済のタグに対してselectイベントを発行した場合にも呼び出されるようである。
このことを利用し「update_data」ボタンをクリックした際の処理を以下のようにすることで、templateSelection関数が呼び出され、タグの見た目が更新される。

```javascript
function update_data(id){
    const selections = $('.' + id).select2('data').map((element) => element.id);
    for(var i =0; i<selections.length;i++){
        $("." + id).select2("trigger", "select", {data: { id: selections[i] } });					
    }
}		
```

これによって、入力フィールドにタグに付ける接頭語を入力し、「update_data」ボタンをクリックすることで、設定済のタグ「北海道」に対し、接頭語★をつけることができる(以下参照)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/c994c8cf-7ef9-4932-069a-dd6bb78bcb68.png)


ただし、templateSelectionは、1つでもタグが追加されると、全てのタグに対して呼び出されるため、どれか1つのタグだけ処理をさせたい場合は、templateSelection の中で特定の条件を満たす場合のみ更新するような処理を書いておき、更新前に見た目を更新させたいタグに特定の処理を施しておくといった対応が必要だろう。
まぁ、Select2でそこまで面倒な要件がでることはないと思うが...

# ソース
最後にhtml側の全体のソースを添付しておく。サーバ側は [Select2でAjaxによりリモートデータを取得する最小サンプルを解説](https://qiita.com/kimisyo/items/f8fb1dbbcc9d7b9a9e33) の記事を参照してほしい。

```html
html>
<head>
<link href="https://code.jquery.com/jquery-3.2.1.min.js" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<!---
<script src="./select2/js/select2.js"></script>
-->
<script>
	function clear_form(id){
		$("." + id).val(null).trigger("change");
	}
	function get_data(id){
		const selections = $('.' + id).select2('data')
		.map((element) => element.id);
		alert(selections)
	}	
	function update_data(id){
		const selections = $('.' + id).select2('data').map((element) => element.id);
		for(var i =0; i<selections.length;i++){
			$("." + id).select2("trigger", "select", {data: { id: selections[i] } });					
		}
	}		
	function add_data(id){
		$("." + id).select2("trigger", "select", {data: { id: "埼玉県" , text: "埼玉県"} });		
	}		
	function open_window(num){
		var handler = window.open( './child' + num + '.html', "windows1", "width=500,height=400");	
	}			
	function setup_select2(id){
		$("." + id).select2({
			ajax: {
				url: "http://localhost:8889/",
				dataType: 'json',
				delay: 250,
				data: function(params){
					return {
						//　ここに書いたパラメータでサーバに渡される。
						keyword: params.term,
					}
				}
				,
				processResults: function (data, params){
					// data.prefecturesに結果配列が格納されている想定
					results = $.map(data.prefectures, function (prefecture) {
						return {
							id: prefecture.id,
							text: prefecture.text
						}
					})
					return {
						results: results
					}
				}
			},
			minimumInputLength: 0,
			language: 'ja',	  
			allowClear: false,
			templateSelection:function(param){
				return document.getElementById("prefix").value + param.text
			}
		})	
	}	

</script>
</head>
<body>
	<from>
	タグに付ける接頭語　<input type="text" id="prefix"/><br/><br/>
	<select class="js-example-basic-multiple1" name="states[]" multiple="multiple" style="width: 100%"></select>
	<input type="button" onclick='javascript:clear_form("js-example-basic-multiple1");' value="clear">
	<input type="button" onclick='javascript:get_data("js-example-basic-multiple1");' value="get_data">
	<input type="button" onclick='javascript:update_data("js-example-basic-multiple1");' value="update_data">
	<input type="button" onclick='javascript:add_data("js-example-basic-multiple1");' value="add_data">
	<input type="button" onclick='javascript:open_window(1);' value="open_window">
	</form>
<script>
	
	$(document).ready(function() {
		setup_select2('js-example-basic-multiple1');
		setup_select2('js-example-basic-multiple2');
		// 初期設定サンプル
    	$(".js-example-basic-multiple1").select2("trigger", "select", {data: { id: "北海道" , text: "北海道"} });
	});
</script>
</body>
```