プログラマブログ

by wacul

menu
  • プログラマ
  • AngularJSのディレクティブの実行順に注意しよう

2014.08.15AngularJSのディレクティブの実行順に注意しよう

TL;DR

ディレクティブのコントローラー内で$element.controllerを使って 他のコントローラーを参照するしようとして失敗したときはpriorityプロパティの値を一度確認してみよう。

ディレクティブの実行順の罠

AngularJSのディレクティブを作るときに何かしらの動作を与えようとする場合、 殆どのケースではlinkプロパティを使うと思います。 しかしながら、controllerプロパティにコントローラーを設定するケースもあるにはあります (階層的なディレクティブを作る等々)。 コントローラーから他のコントローラーを参照するときはDIした$elementのメソッドである$element.controllerを使いますが ここで何も考えずに参照しようとすると失敗することがあります。 よくあるパターンとしてngModelControllerを自作のディレクティブから参照する例を見てみましょう。 以下の例ではaaa、zzzという名前のディレクティブを作ってそれらのコントローラーからngModelControllerを参照しようとしていますが aaaの方では失敗しています。

DEMO

1
2
<aaa ng-model="foo"></aaa>
<zzz ng-model="bar"></zzz>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// アルファベット順で < "ngModel"
app.directive("aaa", function() {
  return {
    restrict: 'E',
    replace: true,
    scope: true,
    priority: -1,
    template: '<div>aaa hasNgModel: </div>',
    controller: function($scope, $element) {
      // ngModelControllerがない!
      $scope.hasNgModel = !!$element.controller('ngModel');
    }
  };
});

// アルファベット順で > "ngModel"
app.directive("zzz", function() {
    return {
    restrict: 'E',
    replace: true,
    scope: true,
    template: '<div>zzz hasNgModel: </div>',
    controller: function($scope, $element) {
      // ngModelControllerがある!
      $scope.hasNgModel = !!$element.controller('ngModel');
    }
  };
});

同じ要素にディレクティブが複数登録されている場合、以下のルールで実行順が決定されます。

  1. priorityプロパティがより大きいディレクティブから実行される
  2. priorityプロパティが同じ場合はアルファベット順で実行される

上の例ではngModelディレクティブはpriorityが0で、新たに定義する場合もデフォルトでは0になるので、 アルファベット順でディレクティブが実行されます。 つまりディレクティブの名前がaaaだと先にそちらが実行されてしまうので、 そのコントローラー内で$element.controller('ngModel')として参照しようとしてもまだ存在していないことになります。 これは意図通りの挙動ではないですよね。

priorityを設定しよう

ディレクティブの実行順をこちら側で制御するためにはpriorityプロパティに適切な数値を設定します。 ngModelディレクティブより後に実行させたいので0以下の数値を入れれば期待どおりの結果を得ることができます。

DEMO

1
2
3
4
5
6
7
8
9
10
11
12
app.directive("aaa", function() {
  return {
    restrict: 'E',
    replace: true,
    scope: true,
    priority: -1, // ここを設定する
    template: '<div>aaa hasNgModel: </div>',
    controller: function($scope, $element) {
      $scope.hasNgModel = !!$element.controller('ngModel');
    }
  };
});

まとめ

  • linkプロパティを使う場合はディレクティブの実行順はあまり気にしなくてもいいが、controllerプロパティを使うときは注意する
  • 同じ要素でpriorityプロパティが同じ場合はアルファベット順に実行される
  • priorityプロパティを設定することで実行順を制御することができる

参考

この記事を書いた人kato

加藤です。JavaScriptに興味があります。

waculでは、プログラマを募集しています。

現在はプロダクトとして、課題発見から改善提案まで自動で行うWeb改善プラットフォーム「AIアナリスト」を開発中です。

waculの採用情報へ

ページトップへ