angular.jsをやってみる (3) - directive -

2013-10-26T00:00:00+00:00 angular.js JavaScript

http://docs.angularjs.org/guide/directive を読みつつドキュメント読みを遂行してみる

directive

<span alert>alert!!</span>

的だったり

<pane>alert!!</pane>

っていうようなタグ(エレメント)で定義したりした場合にangularによるコンパイルを行なって動的な処理を組み込む的な事をサポートする機能みたいな物っぽい。

app.html

<html>
  <head>
    <script src="http://code.angularjs.org/1.0.8/angular.js"></script>
    <!-- SampleController -->
    <script type="text/javascript" src="sample.js"></script>
  </head>
  <body>
    <div ng-controller="sample1">
      <input type="text" ng-model="name"/>
      <ul class="entries">
        <li ng-repeat="sample in samples | filter:name">
          <!-- alertっていう属性をangular.directiveで処理してclickイベントを登録して処理するようにする -->
          <span ng-bind-template="text: {% raw %}{{sample.text}}{% endraw %}" alert></span>
        </li>
      </ul>
    </div>
    <script type="text/javascript" src="app.js"></script>
  </body>
</html>

コメントで書いてる通りなので

sample.js

var SampleController = (function () {
  function SampleController($scope) {
    $scope.samples = [
      { id: 1, text: "hoge" },
      { id: 2, text: "fuga" },
      { id: 3, text: "foobar" }
    ];

    $scope.doClick = function(attr) {
      if ("ngBindTemplate" in attr) alert(attr.ngBindTemplate);
    };
  }

  return SampleController;
})();

ただdoClickメソッドを生やしただけ

app.js

angular.element(document).ready(function() {
  var app = angular.module("hogeApp", []);
  app.controller("sample1", SampleController);

  app.directive(
    "alert",
    function() {
      return function(scope, element, attr) {
        element.bind("click", function() { scope.doClick(attr); });
      };
    }
  );

  angular.bootstrap(document, ["hogeApp"]);
});

単にapp(angular).directiveでdirective名とそれを利用した際に発生する第2引数なコールバックにて関数を返す事でそのエレメントに処理を追加したりスコープやらにごちゃごちゃと出来るっていう

この場合だとっていうようなエレメントにclickイベントを登録して、クリックした際にscopeにあるdoClickを呼び出すっていう事を処理しているだけ

でangular.directiveの第2引数から返せる値は関数であるかオブジェクトであるかを指定出来る。上記では関数を返したけどオブジェクトの場合(別途なdirective)には

angular.element(document).ready(function() {
  var app = angular.module("hogeApp", []);
  app.controller("sample1", SampleController);

  app.directive(
    "pane",
    function() {
      return {
        // restrictはEACMの文字を指定する。組み合わせても問題無い、デフォルトはAでAttributeのみ(後術している所参照)
        restrict: "E",

        scope: { "text": "@" },

        // このdirectiveを使用するにあたって別途なdirectiveが必要な場合にはrequireに指定すれば良い。例えばng-modelが必要ならngModelを指定する等

        require: [],

        //template: "<span>{% raw %}{{text}}{% endraw %}</span>",

        // templateとして使用するURLを指定XHRで取得される
        templateUrl: "template.html",

        // デフォルトはfalse。trueにすると元なエレメントを書き換えされる模様
        replace: true,

        controller: function($scope, $element, $attrs, $transclude) {
          if (!("$parent" in $scope))
            return;

          var parent = $scope.$parent;

          if (!("sample" in parent))
            return;

          var sample = parent.sample;

          if (!("id" in sample))
            return;

          $scope.dataId = sample.id;
        },

        compile: function(tElement, tAttrs, trasclude) {
          /* postLinkのみであればそのままFucntionを返せば良い
          return function(scope, element, attrs, controller) {
            element.css("color", "blue");
          };
          */

          return {
            // preを定義している場合にdirectiveから返すオブジェクトにrequireパラメータが設定されてないとエラーが起きる
            pre: function(scope, element, attr, ngModels) {
              element.css("color", "red");
            },
            post: function(scope, element, attr, controller) {
              element.attr("data-id", scope.dataId);
            }
          };
        },

        // compileが定義されていると作用しない。これはドキュメントに「This property is used only if the compile property is not defined.」と明記されている
        link: function(scope, element, attr, controller) {
          element.css("color", "red");
          element.attr("data-id", scope.dataId);
        }
      };
    }
  );

  angular.bootstrap(document, ["hogeApp"]);
});

っていうような感じかと。で先程関数を返した場合には

app.directive("alert", function() {
  return {
    link: function(scope, element, attr, controller) {
      // 省略
    }
  }
});

的な事をしているのと同じかと。んで更に上記コード中にも書いてるけどrestrictに関しては

  • E: Element
  • A: Attribute
  • C: Class
  • M: Comment

で指定出来る。デフォルトは書いてる通りAなので、関数を返した場合にはデフォルトのAが使われて結果E方式なdirectiveは使用できないんじゃないかと。そこに伴うソースをまだ追えていないので憶測ですが

で上記のdirectiveだと

<pane text="{% raw %}{{sample.text}}{% endraw %}"></pane>

的に利用できる。で上記ではtemplateUrlを指定しているが内容はコメントアウトしているtemplate属性と同じ。でコンパイル後には

<span text="..." ng-model="name" class="ng-pristine ng-valid ng-binding" data-id="5" style="color: red;">...</span>

的な感じでコンパイルされる。のtext属性はscope属性で指定している通りにscopeに格納されて利用できたはず(忘れた)。@や&とか=で指定出来る。後術しているリファレンスを見た方が良いかと

んまぁこのオブジェクト形式で返せる値の属性に関する件はng.$compileに書かれているので、困ったらこれ読めば良いはずなので(ry

若干微妙な所もあるので色々追記・修正するかも

supervisord (1) protractorを使ったangular.jsのe2eテスト