AngularJSカスタムディレクティブ:テンプレート、スコープ、およびトランスクルード
⚡ スマートサマリー
AngularJS のカスタムディレクティブは、自分で定義した再利用可能な動作で HTML を拡張します。ディレクティブファクトリは定義オブジェクトを返し、AngularJS はマークアップ内の対応する要素、属性、クラス、またはコメントが出現する場所にその動作を適用します。
AngularJSにおけるカスタムディレクティブとは何ですか?
A カスタムディレクティブ AngularJS の は、自分で記述した動作で HTML を拡張するユーザー定義ディレクティブです。これは、 モジュール 「directive」関数を使用すると、AngularJS はページ内の一致する要素または属性が表示される場所にそれを添付します。AngularJS には多くの強力な機能がありますが、 組み込みディレクティブ 既成概念にとらわれない、例えば NGリピート場合によっては、カスタムディレクティブが必要になります。
⚠️ 訂正: 元の記述では、カスタムディレクティブは「それが使用される要素を置き換える」とされていました。デフォルトでは、そうはなりません。 template 挿入されます 内部 一致する要素。交換が必要 replace: trueAngularJS 1.3以降は非推奨です。
⚠️ サポートに関するお知らせ: AngularJSが到達 2021年12月31日をもって販売終了となります。 今後、パッチやセキュリティ修正は提供されません。以下の内容は、既存のAngularJS 1.xコードの保守には引き続き有効です。最新のAngularでは、これに相当するものはコンポーネントと属性ディレクティブです。
カスタムディレクティブを使用する理由は、繰り返し処理です。同じマークアップやDOM構造が複数の画面に表示される場合、ディレクティブを使用することで、それらを1つの名前付きタグに変換できます。
カスタム ディレクティブを作成するにはどうすればよいですか?
それでは、AngularJSのカスタムディレクティブを作成する方法の例を見てみましょう。
今回のカスタムディレクティブは、呼び出されたときに「Angular JS Tutorial」というテキストを含むdivタグをページに挿入するだけのシンプルなものです。
以下の注釈付きスクリーンショットは、リストの4つの部分に番号を付けており、その後に説明が続きます。
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { template: '<div>Angular JS Tutorial</div>' } }); </script> </body> </html>
⚠️ 警告 — この出品には欠陥が残されています: コードは公開されたとおりに再現されています。ここに掲載されているすべてのコードには、以下の3つの欠陥が共通して見られます。 chrset="UTF 8" でなければなりません charset="UTF-8"; angular.js (NAIST) と angular.min.js 両方ともロードされるため、AngularJS が 2 回ロードされます。 angular-route.js 依存するファイルよりも先に読み込まれる。
Code 説明
- 最初に作成しているのは、 モジュール AngularJSアプリケーションの場合。カスタムディレクティブを作成するには、このモジュールを使用する必要があります。ディレクティブはこのモジュールを使用して作成されるためです。
- 私たちは今、「ng」というカスタムディレクティブを作成していますGuruそして、私たちの指示書のためのカスタムコードを保持するファクトリ関数を定義します。
- ここでは、AngularJSがカスタムディレクティブ用に定義したパラメータであるテンプレートパラメータを使用しています。このパラメータでは、このディレクティブが使用されるたびに、テンプレートの値を使用して呼び出し元のコードに挿入するように定義しています。
- ここで、独自に作成した「ng-guru」ディレクティブを使用します。これを行うと、テンプレートに定義した値、
<div>Angular JS Tutorial</div>は、ここに注入されます。
指示命名と正規化
名前の不一致 JavaスクリプトとHTMLはほとんどの初心者を混乱させる。AngularJS 正規化する マッチング前にすべての要素と属性名を削除します。 x- or data- 接頭辞、次に、で区切られた名前を変換します。 -, : or _ キャメルケースに変換します。それが理由です。 ngGuru マッチ ng-guru、 なぜ data-ng-guru これも有効です。キャメルケースが正式名称で、ケバブケースは単にHTMLでの表記方法です。
⚠️ 訂正: 元のテキストには、カスタムディレクティブ名は「文字『ng』で始まるべきである」と書かれていた。 AngularJSの公式ドキュメント 反対のことを勧めています。自分の指示には決して接頭辞を付けないでください。 ngまたは、将来の組み込み機能と衝突する可能性があります。独自のプレフィックスを使用してください。例: g99Guru として書かれました g99-guru.
コードが正常に実行されると、この出力が表示されます。
出力:
出力結果から、カスタムテキストを表示するためのテンプレートが定義された、独自に作成したng-guruディレクティブがブラウザに正しく表示されていることが明確に分かります。次のステップは、そのテンプレート内に再利用する価値のあるコードを追加することです。
再利用可能なディレクティブを作成する方法
カスタム ディレクティブの威力についてはすでに説明しましたが、再利用可能な独自のディレクティブを構築することで、それを次のレベルに進めることができます。
例えば、複数の画面にわたって常に以下のHTMLタグを表示するコードを挿入したいとしましょう。これは基本的に、ユーザーの「名前」と「年齢」を入力するためのものです。
このフラグメントを毎回コーディングすることなく複数の画面で再利用するために、AngularJSでマスターコントロール、つまりディレクティブを作成し、これら2つのコントロールを保持します。
そこで、これまで各画面で同じマークアップを繰り返していたのを、ディレクティブに埋め込んで、そのディレクティブを呼び出すようにしました。下のスクリーンショットは、変更された唯一の行を示しています。
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { template: ' Name <input type="text"><br><br> Age<input type="text">' }; }); </script> </body> </html>
Code 説明
- カスタムディレクティブのコードスニペットでは、テンプレートパラメータに渡される値のみが変更されます。通常のタグやテキスト行の代わりに、ページに表示する必要のある「名前」と「年齢」の2つの入力コントロールのフラグメント全体を入力しています。
💡 ヒント: これほど長いテンプレートは、専用のファイルに格納すべきです。 template の templateUrl: 'name-age.html'.
コードが正常に実行されると、この出力が表示されます。
出力:
上記の出力から、カスタムディレクティブのテンプレートからコードスニペットがページに追加されていることがわかります。テンプレートはまだ静的なので、次のステップはデータを与えることです。
AngularJSのディレクティブとスコープ
その スコープ は、ビューとコントローラー間のデータを管理することで、コントローラーとビューを結びつける接着剤として定義されます。
カスタムAngularJSディレクティブを作成する場合、デフォルトでは親コントローラのスコープオブジェクトにアクセスできます。これはコピーではなく、同じスコープオブジェクトです。
このようにして、カスタム ディレクティブがメイン コントローラーに渡されるデータを利用することが容易になります。
親コントローラーのスコープをカスタムディレクティブで使用する方法の例を見てみましょう。以下のスクリーンショットは、4つのステップを示しています。
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp" ng-controller="DemoController"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.controller('DemoController',function($scope) { $scope.tutorialName = "Angular JS"; }); app.directive('ngGuru',function(){ return { template: '<div>{{tutorialName}}</div>' } }); </script> </body> </html>
Code 説明
- まず、「DemoController」という名前のコントローラーを作成します。その中で、tutorialNameという変数を定義し、それをスコープオブジェクトに1つのステートメントで関連付けます。
$scope.tutorialName = "Angular JS". - カスタム ディレクティブでは、式を使用して変数「tutorialName」を呼び出すことができます。 この変数は、このディレクティブの親となるコントローラー「DemoController」で定義されているため、アクセス可能です。
- div タグでコントローラーを参照し、親 div タグとして機能します。 カスタム ディレクティブがtutorialName変数にアクセスするには、これを最初に行う必要があることに注意してください。
- 最後に、カスタム ディレクティブ「ng-guru」を div タグに添付するだけです。
コードが正常に実行されると、この出力が表示されます。
出力:
上記の出力から明らかなように、カスタムディレクティブ「ng-guru」は、親コントローラのスコープ変数tutorialNameを使用しています。親スコープを共有することは便利ですが、必ずしも望ましいとは限りません。
ディレクティブを含むコントローラーの使用
AngularJS は、 カスタム ディレクティブからコントローラーのメンバー変数に直接アクセスします スコープ オブジェクトは必要ありません。
アプリケーションには複数のコントローラに属する複数のスコープオブジェクトが存在する場合があり、誤って間違ったコントローラのスコープオブジェクトにアクセスしてしまう可能性が高いため、このような対策が必要になることがあります。
このような場合、ディレクティブから「このコントローラーにアクセスしたい」と具体的に指示する方法があります。
それを実現する方法の例を見てみましょう。 スクリーンショットには、4つの手順に番号が振られています。
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp" ng-controller="DemoController"> <div ng-guru99=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.controller('DemoController',function() { this.tutorialName = "Angular"; }); app.directive('ngGuru99',function(){ return { controller: 'DemoController', controllerAs: 'ctrl', template: '{{ctrl.tutorialName}}' }; }); </script> </body> </html>
Code 説明
- まず、「DemoController」という名前のコントローラーを作成します。このコントローラー内で、「tutorialName」という変数を定義します。今回は、スコープオブジェクトにアタッチするのではなく、コントローラーに直接アタッチします。
- カスタム ディレクティブでは、コントローラー パラメーター キーワードを使用して、コントローラー「DemoController」を使用することを具体的に示しています。
- 「controllerAs」パラメータを使用して、コントローラへの参照を作成します。これはAngularJSで定義されているもので、エイリアスを使用してコントローラを参照する方法です。
- 最後に、テンプレートでは、ステップ3で作成した参照を使用し、ステップ1でコントローラに直接アタッチしたメンバ変数を使用します。
注意: ディレクティブ内で複数のコントローラにアクセスするには、コントローラ、controllerAs、およびテンプレートステートメントのそれぞれのブロックを指定します。 親 ディレクティブのコントローラーの代わりに、 require.
コードが正常に実行されると、この出力が表示されます。
出力:
出力結果から、カスタムディレクティブが DemoController およびそれに紐づくメンバ変数 tutorialName にアクセスし、「Angular」というテキストを表示していることが明確にわかります。どちらの方法も共有スコープから読み取りを行っていますが、分離スコープを使用すればこれを防ぐことができます。
分離スコープにおける @、=、&、< のバインディングの違い
親スコープを共有するディレクティブは、コントローラごとに一度しか使用できません。なぜなら、2つのコピーが同じ変数を巡って競合するからです。 scope オブジェクトは 分離株の範囲このディレクティブは、リストされたプロパティ以外、外部の情報は何も参照しません。各エントリは、その値がどのように伝達されるかを示す接頭辞記号を使用します。
| シンボル | リーダーシップ | 受け取った価値 | に使用します |
|---|---|---|---|
| @ | 一方通行 | 補間文字列としての属性 | ラベル、タイトル、プレーンテキスト |
| = | 双方向の | 生きた物体または価値そのもの | ディレクティブが書き返すモデル |
| & | 外への呼びかけ | 外側のスコープ内の式を評価する関数 | on-closeやon-saveなどのコールバック |
| < | 一方通行 | 評価された値は、決して上方修正されることはない。 | 読み取り専用データ(AngularJS 1.5で追加) |
💡 ヒント: リーチ < =一方向バインディングは監視コストが低く、コンポーネントが呼び出し元のデータを密かに変更するのを防ぎます。次のセクションでは、 @.
AngularJS ディレクティブとコンポーネント: ng-transclude
先に述べたように、AngularJSは、 HTML。 そして、再利用可能なカスタム ディレクティブを使用してコード インジェクションを行う方法をすでに見てきました。
しかし、現代のウェブアプリケーション開発には、開発の概念も存在します。ping ウェブコンポーネントとは、基本的に、コード内でコンポーネントとして使用できる独自のHTMLタグを作成することを意味します。
したがって、AngularJSは、呼び出し元がタグ内に記述するあらゆるマークアップをラップする機能を提供することで、HTMLタグを拡張するための新たなレベルのパワーを提供します。
これは「」によって行われます。ng-トランスクルード」ディレクティブは、AngularJS にマークアップ内のカスタム タグ内に記述されたすべてのものをキャプチャし、その時点でテンプレートに挿入するように指示します。
これを実現する方法の例を見てみましょう。スクリーンショットには、以下の5つのポイントが番号付きで示されています。
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <pane title="{{title}}">Angular JS</pane> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('pane',function(){ return { transclude:true, scope :{title:'@'}, template: '<div style="border: 1px solid black;"> '+ '<ng-transclude></ng-transclude>'+ '</div>' }; }); </script> </body> </html>
Code 説明
- このディレクティブを使用して、「pane」という名前のカスタムHTMLタグを定義し、ファクトリ関数からそのコードを取得します。出力では、paneタグは実線の黒い枠線で囲まれた四角形の中に「Angular JS」というテキストを表示します。
- 「transclude」プロパティをtrueに設定する必要があります。これにより、AngularJSは元の内部マークアップを保持し、それを利用可能にします。
ng-transclude. - スコープオブジェクトでは、タイトルバインディングを宣言しています。属性は通常、名前/値のペアとして記述されます。
name="value"今回の場合、pane タグの属性は「title」であり、「@」記号は AngularJS にその属性を補間文字列としてコピーするように指示します。 - テンプレートは、コントロール部分に実線の黒い枠線を描画し、挿入されたマークアップが挿入される場所を示します。
- 最後に、定義済みのタイトル属性とともに、カスタムHTMLタグを呼び出します。
⚠️ 訂正: 原文では「@」は「Angularからの要件」と説明されています。これは形式的なものではありません。 @ は、上記の表にある 4 つの分離スコープバインディングシンボルの 1 つであり、常に文字列を返します。このテンプレートは、 {{title}}コントローラーは定義していません titleそのため、挿入されたテキストのみが表示されます。
コードが正常に実行されると、この出力が表示されます。
出力:
- 出力には、カスタムペイン要素が枠線付きのボックスとしてレンダリングされ、その中に「Angular JS」というテキストが埋め込まれていることが示されています。
指令は他の指令を含むこともでき、そうしてより大きなコンポーネントが組み立てられる。
ネストされたディレクティブ
AngularJS のディレクティブはネストできます。 プログラミング言語、相互にディレクティブを埋め込む必要がある場合があります。
以下の例を見ると、これについてよりよく理解できます。
この例では、「outer」と「inner」という 2 つのディレクティブを作成しています。
- inner ディレクティブは、「Inner」というテキストを表示します。
- 一方、外側のディレクティブは実際には内側のディレクティブを呼び出して、「Inner」というテキストを表示します。
以下のスクリーンショットは、両方のディレクティブの定義と、一方が他方を呼び出す箇所を示しています。
</head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <outer></outer> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('outer',function(){ return { restrict:'E', template: '<div><h1>Outer</h1><inner></inner></div>', }}); app.directive('inner',function(){ return { restrict:'E', template: '<div><h1>Inner</h1></div>', } }); </script> </body> </html>
⚠️ 警告 — この出品には欠陥が残っています: 掲載リストは以下から始まります。 </head>オープニングを逃した <!DOCTYPE html>, <html> (NAIST) と <head> 行を追加してください。実行する前に追加してください。
Code 説明
- 親ディレクティブとして動作する「outer」というディレクティブを作成しています。 このディレクティブは、「内部」ディレクティブを呼び出します。
- その
restrict:'E'オプションは、AngularJS に各ディレクティブを一致させるように指示します。 素子 名前なので、タグとして記述できます<outer>(NAIST) と<inner>文字「E」は「Element」という単語の略語です。 - ここでは、div タグにテキスト「Inner」を表示する inner ディレクティブを作成しています。
- 外側のディレクティブのテンプレート内で、内側のディレクティブを呼び出しています。つまり、ここでは内側のディレクティブのテンプレートを外側のディレクティブに挿入しているのです。
- 最後に、外部ディレクティブを直接呼び出しています。
⚠️ 訂正: 原文では、 restrict:'E' 「内部ディレクティブからのデータが外部ディレクティブで利用可能であることを保証するために」必要である。しかし、実際にはそのようなことはせず、単に制御するだけである。 の ディレクティブを記述できます。ネストが機能するのは、コンパイラが挿入したばかりのテンプレートに繰り返しアクセスするためです。データを共有するには、 require (NAIST) と controller.
コードが正常に実行されると、この出力が表示されます。
出力:
出力から、
- 外側のディレクティブと内側のディレクティブの両方が呼び出され、両方のdivタグ内のテキストが表示されていることがわかります。
マークアップのレンダリングは、ディレクティブの役割の半分に過ぎません。残りの半分は、ユーザーの操作に反応することです。
ディレクティブにおけるイベントの処理
イベント マウスのクリックやボタンのクリックといった操作は、ディレクティブ自体の中で処理できます。これは、リンク関数を使用して行います。リンク関数によって、ディレクティブはHTMLページのDOM要素に自身を関連付けることができます。
構文:
リンク関数の構文は以下のとおりです。
link: function ($scope, element, attrs)
リンク関数は通常、スコープ、ディレクティブが関連付けられている要素、および対象要素の属性という3つのパラメータを受け取ります。さらに、必要なコントローラとトランスクルード関数という2つのパラメータは、適用される際に渡されます。
コンパイルフェーズとリンクフェーズの比較
AngularJSはディレクティブを2段階で処理するため、どちらの段階がどちらの段階かを把握することで、自分のコードがどこに属すべきかが分かります。
- コンパイル テンプレート要素に対して、スコープが存在する前に一度だけ実行されます。これは、すべてのクローンで共有される構造的な編集に適しています。
- リンク テンプレートが複製され、スコープがアタッチされた後、インスタンスごとに一度実行されます。DOMリスナーとスコープ読み取りはここに記述します。
コンパイルにはスコープがないため、データが必要なものはすべてリンクに属します。そのため、以下の例では次のように定義しています。 link 単独で。
これを実現する方法の例を見てみましょう。 スクリーンショットには、以下の4つのポイントに番号が振られています。
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru="">Click Me</div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { link:function($scope,element,attrs) { element.bind('click',function () { element.html('You clicked me'); });} }}); </script> </body> </html>
Code 説明
- AngularJSで定義されているlink関数を使用して、ディレクティブがHTML DOM内のイベントにアクセスできるようにしています。
- ここでは、HTML DOM要素(この場合は「div」要素)のイベントに応答したいので、「element」キーワードを使用しています。次に、「bind」関数を使用して、要素のクリックイベントにカスタム機能を追加します。「click」は、あらゆるHTMLコントロールのクリックイベントを示すために使用されるキーワードです。
- ここでは、要素(この場合はdiv要素)の内部HTMLを「You clicked me」というテキストに置き換えたいと伝えています。
- ここでは、ng-guru カスタム ディレクティブを使用するように div タグを定義しています。
💡 ヒント: ディレクティブは、後始末を自身で行うべきです。内部の要素よりも長く存続するリスナーはすべて削除してください。 element.on('$destroy', ...)あるいは、ページがメモリリークを起こしている。
コードが正常に実行されると、この出力が表示されます。
出力:
- 最初は、divタグ内に定義されている「Click Me」というテキストがユーザーに表示されます。divタグをクリックすると、代わりに以下の出力が表示されます。
上記の例はすべて、同じ定義オブジェクトのプロパティを1つか2つ使用しています。完全なセットは覚えておく価値があります。ping 手元に。
ディレクティブ定義オブジェクトプロパティ参照
ディレクティブファクトリによって返されるオブジェクトは ディレクティブ定義オブジェクト各プロパティは、ディレクティブの動作に関する1つの質問に答えます。
| プロパティ | デフォルト | 何それがありません |
|---|---|---|
| 制限する | 「EA」 | 以下の形式のうちどれが一致しますか: E 要素、A 属性、C クラス、M コメント |
| template | なし | 一致する要素に挿入されたインラインマークアップ |
| テンプレートURL | なし | テンプレートファイルのパス。一度取得後、キャッシュされます。 |
| スコープ | false | false は親スコープを共有し、true は子を作成します。オブジェクトは分離します |
| コントローラ | なし | API を公開するコンストラクタは、他のディレクティブが要求できる |
| コントローラーとして | なし | テンプレート内のコントローラーのエイリアス |
| 必要とする | なし | 別のディレクティブのコントローラーを挿入します。^ は親を検索し、^^ は自身をスキップし、? はオプションです。 |
| なし | インスタンスごとの関数: スコープ、要素、属性、コントローラー、transcludeFn | |
| コンパイル | なし | スコープが存在する前にテンプレート上で一度実行されます |
| 包含する | false | ng-transcludeの元の内部マークアップを保持します |
| 優先順位 | 0 | 共有要素のコンパイル順序。上位要素が先に実行されます。 |
| ターミナル | false | その要素に対する優先度の低い指示を停止します |
| replace | false | テンプレートと要素を入れ替えます。バージョン1.3以降非推奨です。 |
💡 ヒント: で始まる restrict, template (NAIST) と link、 加えます scope 再利用のため、 controller (NAIST) と require 指令間の通話については、 priority (NAIST) と terminal 単独で。

















