リンクvsコンパイルvsコントローラー


529

ディレクティブを作成すると、コードをコンパイラー、リンク関数、またはコントローラーに配置できます。

ドキュメントでは、彼らはそれを説明しています:

  • コンパイルおよびリンク機能は、角サイクルのさまざまなフェーズで使用されます
  • コントローラーはディレクティブ間で共有されます

ただし、私にとっては、どの種類のコードをどこに配置すればよいかは明らかではありません。

例:コンパイルで関数を作成し、それらをリンクのスコープにアタッチすることや、コントローラーのスコープに関数のみをアタッチすることはできますか?

各ディレクティブが独自のコントローラーを持つことができる場合、コントローラーはディレクティブ間でどのように共有されますか?コントローラは本当に共有されているのですか、それともスコープのプロパティだけですか?




1
ディレクティブのライフサイクル(作成フェーズ)の図を投稿しました。:多分それは誰か助けfilimanjaro.com/2014/...
平均ジョー

回答:


470

コンパイル:

これは、Angularが実際にディレクティブをコンパイルするフェーズです。このコンパイル関数は、指定されたディレクティブへの参照ごとに1回だけ呼び出されます。たとえば、ng-repeatディレクティブを使用しているとします。ng-repeatは、アタッチされている要素を検索し、アタッチされているhtmlフラグメントを抽出して、テンプレート関数を作成する必要があります。

HandleBars、アンダースコアテンプレートまたは同等のものを使用した場合は、テンプレートをコンパイルしてテンプレート関数を抽出するようなものです。このテンプレート関数にデータを渡します。その関数の戻り値は、適切な場所にデータがあるHTMLです。

コンパイル段階は、テンプレート関数を返すAngularでのステップです。この角度のテンプレート関数はリンク関数と呼ばれます。

リンクフェーズ:

リンクフェーズでは、データ($ scope)をリンク関数にアタッチし、リンクされたhtmlを返します。ディレクティブは、このhtmlの移動先または変更内容も指定しているため、移動することはすでに適切です。これは、リンクされたhtml、つまり既にデータが添付されているhtmlに変更を加える機能です。リンク関数でコードを記述した場合、通常はそのリンク後関数(デフォルト)で角度が付けられます。これは、リンク関数がデータをテンプレートにリンクした後に呼び出されるコールバックの一種です。

コントローラー:

コントローラは、ディレクティブ固有のロジックを配置する場所です。このロジックはリンク機能にも含めることができますが、そのロジックをスコープに配置して「共有可能」にする必要があります。これの問題は、実際には予期されるものではないディレクティブのものでスコープを破損することです。では、2つの指令が互いに話したり、協力したりする場合の選択肢は何ですか?もちろん、そのロジックをすべてサービスに入れて、これらの両方のディレクティブをそのサービスに依存させることもできますが、これはもう1つの依存関係をもたらすだけです。別の方法は、このスコープにコントローラーを提供することです(通常はスコープを分離しますか?)。このディレクティブが他のディレクティブを "必要とする"場合、このコントローラーは別のディレクティブに挿入されます。


67
明確にするために:compileは、ページ全体で使用されるテンプレートをコンパイルします。リンカーは各インスタンスに関連付けられています。正しい?その後、コントローラーはインスタンス間で機能します。
Zlatko

4
各ディレクティブcontroller関数の@CMCDragonkai はコンパイル、ローカルDOMツリーブランチの pre-linkに実行されます。またcontrollerpre-link関数はローカルDOMブランチを上から下にトラバースして実行さます。その後post-linkボトムアップで実行されます。
Artem Platonov 2014

9
あなたがそれを理解しなければ、それはただの混乱です。それがすることをする理由があります。
demisx 2015

3
これは正しい技術的な答えですが、リンク機能をいつ使用するべきかについてはまだ質問が残っています。
ニコラスマーシャル

2
我々は、使用しなければならないcontroller代わりにlinkどこでも?そのため、メソッドを共有する必要がある場合や、ロジックを導入する必要がある場合に、将来コードを変更する必要がないようにします。controllerリンクの代わりに常に使用することに落とし穴はありますか?
JPS

99

また、GoogleチームによるO'Reily AngularJSの本の内容を追加したいと思います。

コントローラー-ディレクティブ間で通信するためのAPIを公開するコントローラーを作成します。良い例は、Directive to Directive Communicationです。

リンク-結果のDOM要素インスタンスをプログラムで変更し、イベントリスナーを追加し、データバインディングを設定します。

コンパイル-ng-repeatで使用する場合と同様に、ディレクティブのコピー全体の機能のDOMテンプレートをプログラムで変更します。コンパイル関数は、リンク関数を返して、結果の要素インスタンスを変更することもできます。


thinkster.ioのリンクは、支払いがないと視聴できません。ない私のリンク、おそらくこれは、より適している:toddmotto.com/directive-to-directive-communication-with-require
R.バンTwisk

51

Aをdirective使用すると、Webコンポーネントを構築するための宣言的な方法でHTMLボキャブラリを拡張できます。ng-app属性は、ディレクティブです、そうですng-controllerと、すべてのng- prefixed attributes。ディレクティブは、することができattributestags偶数かclass namescomments

ディレクティブが生まれる方法(compilationおよびinstantiation

コンパイル:レンダリングする前にDOM compileに対して関数を使用しmanipulatelink関数(リンクを処理します)を返します。これは、instancesこのディレクティブのすべてと共有する必要のあるメソッドを配置する場所でもあります。

リンク:link関数を使用して、特定のDOM要素(テンプレートから複製されたもの)のすべてのリスナーを登録し、ページへのバインディングを設定します。

compile()関数に設定されている場合、それらは一度だけ設定されていたはずです(多くの場合、これが必要です)。設定した場合link()の機能、それらはHTML要素がデータにバインドされるたびに設定される オブジェクトを。

<div ng-repeat="i in [0,1,2]">
    <simple>
        <div>Inner content</div>
    </simple>
</div>

app.directive("simple", function(){
   return {
     restrict: "EA",
     transclude:true,
     template:"<div>{{label}}<div ng-transclude></div></div>",        
     compile: function(element, attributes){  
     return {
             pre: function(scope, element, attributes, controller, transcludeFn){

             },
             post: function(scope, element, attributes, controller, transcludeFn){

             }
         }
     },
     controller: function($scope){

     }
   };
});

Compile関数が戻るprepostリンク機能。リンク前機能には、インスタンステンプレートとのスコープcontrollerがありますが、テンプレートはスコープにバインドされておらず、内容が変換されていません。

Postlink関数は、post linkが最後に実行される関数です。これで、、およびtransclusionが完成the template is linked to a scopeしましたview will update with data bound values after the next digest cyclelinkオプションでは、設定にちょうど近道であるpost-link機能を。

コントローラー:ディレクティブコントローラーは、別のディレクティブリンク/コンパイルフェーズに渡すことができます。これは、指令間の通信で使用する手段として他の指令に注入できます。

必要なディレクティブの名前を指定する必要があります。同じ要素またはその親にバインドする必要があります。名前の前に次を付けることができます。

?  Will not raise any error if a mentioned directive does not exist.
^  Will look for the directive on parent elements, if not available on the same element.

角括弧[‘directive1′, ‘directive2′, ‘directive3′]を使用して、複数のディレクティブコントローラーを要求します。

var app = angular.module('app', []);

app.controller('MainCtrl', function($scope, $element) {
});

app.directive('parentDirective', function() {
  return {
    restrict: 'E',
    template: '<child-directive></child-directive>',
    controller: function($scope, $element){
      this.variable = "Hi Vinothbabu"
    }
  }
});

app.directive('childDirective', function() {
  return {
    restrict:  'E',
    template: '<h1>I am child</h1>',
    replace: true,
    require: '^parentDirective',
    link: function($scope, $element, attr, parentDirectCtrl){
      //you now have access to parentDirectCtrl.variable
    }
  }
});

1
あなたはparentDirectiveCtrlを子のコントローラーに入れる方法を示したと述べました...この例では子にはコントローラーがなく、リンク機能があります...私は現在この問題に固執していないので、そうではないかもしれませんとても重要ですが、奇妙な質問です。
alockwood05 2015年

13

また、コントローラーとリンク関数の両方を使用する理由は(両方ともスコープ、要素、および属性にアクセスできるため)、使用可能なサービスまたは依存関係をコントローラーに(任意の順序で)渡すことができるためです。リンク機能でそれを行うことはできません。署名が異なることに注意してください。

controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...

link: function(scope, element, attrs) {... //no services allowed

2
回答に反対票を投じるときは、コメントを残して、ポイントを説明してください。ありがとう
svassr 2013年

53
私はダウンボーターではありませんでしたが、ディレクティブ自体に必要な依存関係を挿入できるため、これは厳密には正しくありませんmodule.directive('myDirective', function($window) { etc...。例:これは、リンク関数の内部からアクセスできます。
Mike Chamberlain、

1
リンク機能にサービスを挿入できるため、これは簡単に正しくないようです
コードウィスパー

1
@JoshRibakoff最終結果は同じです。リンク機能でサービスにアクセスできます。関数の引数で宣言されているかどうかは関係ありません。この点でマイクチェンバレンは正しい
コナーワイアット、

1
@ cwyatt1私は用語を修正していました。plngrは、Angularが持っている機能ではないため、link()関数への注入を示していません。あなたは私が独断的だと思うかもしれませんが、メタマットのコメントは、そのplunkrが行うことと、コントローラーに注入することが行うことの間の多数の重要な違いをすでに概説しています。OPは違いを尋ねており、違いがあります。
ジョシュリバコフ2015

10

これは、ディレクティブのフェーズを理解するための良いサンプルです http://codepen.io/anon/pen/oXMdBQ?editors=101

var app = angular.module('myapp', [])

app.directive('slngStylePrelink', function() {
    return {
        scope: {
            drctvName: '@'
        },
        controller: function($scope) {
            console.log('controller for ', $scope.drctvName);
        },
        compile: function(element, attr) {
            console.log("compile for ", attr.name)
            return {
                post: function($scope, element, attr) {
                    console.log('post link for ', attr.name)
                },
                pre: function($scope, element, attr) {
                    $scope.element = element;
                    console.log('pre link for ', attr.name)
                        // from angular.js 1.4.1
                    function ngStyleWatchAction(newStyles, oldStyles) {
                        if (oldStyles && (newStyles !== oldStyles)) {
                            forEach(oldStyles, function(val, style) {
                                element.css(style, '');
                            });
                        }
                        if (newStyles) element.css(newStyles);
                    }

                    $scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);

                    // Run immediately, because the watcher's first run is async
                    ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
                }
            };
        }
    };
});

html

<body ng-app="myapp">
    <div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
        <div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
        </div>
    </div>
</body>

4
あなたは、このサンプルコードでは、違いを理解するのに役立つだろう理由について詳しく説明でしたlinkcompilecontroller
2015

require依存ディレクティブのコントローラーにdディレクティブを挿入する方法を知っていますか?
alockwood05 2015

例をコードペンします:キャッチされないエラー:[$ injector:modulerr]次の理由によりモジュールmyappのインスタンス化に失敗しました:エラー:[$ injector:unpr]不明なプロバイダー:slngStylePrelinkProvider
rofrol

7
  • compile:新しい式を追加するなど、ディレクティブテンプレートを変更する必要がある場合に使用し、このディレクティブ内に別のディレクティブを追加します
  • コントローラ:$ scopeデータを共有/再利用する必要があるときに使用されます
  • リンク:イベントハンドラーをアタッチしたり、DOMを操作したりする必要があるときに使用する関数です。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.