ばかおもちゃ本店:Youtube twitter:@sashimizakana Amazon.co.jpアソシエイト

2013年7月22日月曜日

AngularJSでselect2のTagを利用する(AngularUI未使用)

AngularJSでSelect2を利用するときはAngularUIにSelect2がディレクティブ化されたものがあるので、それを使えばいいんだけど、タグ機能がIE8では動かない。もうそりゃぴくりとも動かない。
そもそもAngularUI-Select2の単体テストすらIE8では動作しない。かたやSelect2単体はIE8でも普通動く。
じゃあもうSelect2を自分でディレクティブにしてしまえと思った。

ディレクティブを作るのは再利用できるようにしたりとか、ちゃんとやるとコード量も増えてなかなか難しいけど、使用方法なんかをある程度決めうちしてしまえば割と簡単にできる。ただディレクティブのスコープ周りとかapply、parse周辺は本当にこれで良いのかやや不安なので、より良い方法があるなら是非ご教授いただきたい。

コードは以下の通り。

angular.module('tag',[]).directive("tag", function($parse)
{
    return {
        restrict : 'A'
        ,require:['ngModel']
        ,link : function(scope, element, attr)
        {
            //選択候補
            var tags = [];
            var result = $parse(attr.ngModel);
            //Select2の作成対象
            var input = element.children('input');

            //Select2を作成する。idは比較するときの対象なので
            //textにしておかないと、選択候補と同じタグを新たに作成できてしまう
            input.select2(
            {
                  tags:function(){return tags}
                  ,id:function(i){return i.text}
            });

            input.on("change",function()
            {
                //選択結果を渡す
                result.assign(scope,input.select2('data'));
                //変更結果を適用する
                if(scope.$$phase)
                {
                    scope.$eval(attr.ngModel);
                }
                else
                {
                    scope.$apply(attr.ngModel);
                }   
            });

            //選択候補の監視
            scope.$watch(attr.tag,function(newValue)
            {
                tags = newValue;
            },true);

            //選択状態の監視
            scope.$watch(attr.ngModel,function(newValue)
            {
                input.select2("data",newValue);
            },true);

        }
        ,template : '<input type="text" />'
    };
});

これを<div tag="tags" ng-model="selected">みたいな感じで使う。
本当は<input tag=...>みたいに出来た方がスマートな感じで、AngularUIではそうしているのだけど、Select2が追加するタグのせいでng-showなんかを使うためには一階層上に付けなくてはならなかったりして、結局不便になっているのでこうした。