AngularJS
カスタムディレクティブ
サーチ…
前書き
ここでは、AngularJSのディレクティブ機能について学びます。以下にディレクティブがどのようなものであるかについての情報と、ディレクティブの使い方の基本的な例と高度な例を示します。
パラメーター
パラメータ | 詳細 |
---|---|
範囲 | ディレクティブのスコープを設定するプロパティ。これは、false、true、または独立スコープとして設定できます。{@、=、<、&} |
スコープ:虚偽 | ディレクティブは親スコープを使用します。ディレクティブのスコープが作成されていません。 |
scope:true | ディレクティブは親スコープをプロトタイプ的に新しい子スコープとして継承します。新しいスコープを要求する同じ要素に複数のディレクティブがある場合、新しいスコープを1つ共有します。 |
スコープ:{@} | ディレクティブスコーププロパティのDOM属性値へのバインディングの1つの方法。親でバインドされた属性値は、ディレクティブ範囲で変更されます。 |
スコープ:{=} | ディレクティブ属性が変更された場合は親の属性を変更し、その逆の場合は属性を変更する双方向属性バインディングです。 |
スコープ:{<} | ディレクティブスコーププロパティとDOM属性式の一方向バインディング式は親で評価されます。これは、親値のアイデンティティを監視するので、親のオブジェクトプロパティの変更はディレクティブに反映されません。ディレクティブ内のオブジェクトプロパティの変更は、両方とも同じオブジェクトを参照するため、親に反映されます |
スコープ:{&} | ディレクティブがデータを親に評価される式に渡せるようにします。 |
コンパイル:関数 | この関数は、リンク関数が実行される前にディレクティブテンプレートでDOM変換を実行するために使用されます。 tElement (ディレクティブ・テンプレート)およびtAttr (ディレクティブで宣言された属性のリスト)をtAttr ます。スコープにアクセスすることはできません。 post-link 関数として登録される関数を返すことがあります。また、 pre post-link 関数とpost-link 関数として登録されたpre-link プロパティとpost プロパティを持つオブジェクトを返すことがあります。 |
リンク:関数/オブジェクト | リンクプロパティは、関数またはオブジェクトとして構成できます。 scope(ディレクティブスコープ)、iElement(ディレクティブが適用されるDOM要素)、iAttrs(DOM要素属性のコレクション)、controller(ディレクティブで必要なコントローラの配列)、transcludeFnの各引数を受け取ることができます。これは主に、DOMリスナの設定、変更のモデルプロパティの監視、DOMの更新に使用されます。テンプレートが複製された後に実行されます。コンパイル機能がない場合は独立して設定されます。 |
事前リンク機能 | 子リンク関数の前に実行されるリンク関数。デフォルトでは、子ディレクティブ・リンク関数は親ディレクティブ・リンク関数の前に実行され、プリリンク関数は親が最初にリンクできるようにします。 1つのユースケースは、子が親からのデータを必要とする場合です。 |
ポストリンク機能 | 子要素の後の幹部が親にリンクするリンク関数。イベントハンドラのアタッチや子ディレクティブへのアクセスによく使用されますが、子ディレクティブがすでにリンクされているため、子ディレクティブで必要なデータはここで設定しないでください。 |
restrict:文字列 | DOM内からディレクティブを呼び出す方法を定義します。可能な値(当社ディレクティブ名と仮定するとあるdemoDirective ): E -要素名( <demo-directive></demo-directive> )、 A -属性( <div demo-directive></div> C -マッチングクラス( <div class="demo-directive"></div> )、 M - コメント( <!-- directive: demo-directive --> ) restrict プロパティは複数のオプションをサポートすることもできます。たとえば、 - restrict: "AC" は、ディレクティブをAttributeまたはClassに制限します。省略された場合、 デフォルト値は"EA" (要素または属性)です。 |
require: 'demoDirective' | 現在の要素でdemoDirectiveのコントローラを探し、そのコントローラをリンキング関数の第4引数として注入します。見つからなければエラーを投げる。 |
require: '?demoDirective' | demoDirectiveのコントローラーを見つけようとするか、見つからなければリンクfnにヌルを渡します。 |
require: '^ demoDirective' | 要素とその親を検索して、demoDirectiveのコントローラを探します。見つからなければエラーを投げる。 |
require: '^^ demoDirective' | 要素の親を検索して、demoDirectiveのコントローラを探します。見つからなければエラーを投げる。 |
require: '?^ demoDirective' | 要素とその親を検索してdemoDirectiveのコントローラを見つけようとするか、見つからなければリンクfnにnullを渡します。 |
require: '?^^ demoDirective' | 要素の親を検索してdemoDirectiveのコントローラを探したり、見つからなければリンクfnにnullを渡したりします。 |
カスタムディレクティブの作成と使用
ディレクティブは、anglejの最も強力な機能の1つです。カスタムanglejsディレクティブは、htmlタグに特定の動作を提供するために新しいhtml要素またはカスタム属性を作成することによって、htmlの機能を拡張するために使用されます。
directive.js
// Create the App module if you haven't created it yet
var demoApp= angular.module("demoApp", []);
// If you already have the app module created, comment the above line and create a reference of the app module
var demoApp = angular.module("demoApp");
// Create a directive using the below syntax
// Directives are used to extend the capabilities of html element
// You can either create it as an Element/Attribute/class
// We are creating a directive named demoDirective. Notice it is in CamelCase when we are defining the directive just like ngModel
// This directive will be activated as soon as any this element is encountered in html
demoApp.directive('demoDirective', function () {
// This returns a directive definition object
// A directive definition object is a simple JavaScript object used for configuring the directive’s behaviour,template..etc
return {
// restrict: 'AE', signifies that directive is Element/Attribute directive,
// "E" is for element, "A" is for attribute, "C" is for class, and "M" is for comment.
// Attributes are going to be the main ones as far as adding behaviors that get used the most.
// If you don't specify the restrict property it will default to "A"
restrict :'AE',
// The values of scope property decides how the actual scope is created and used inside a directive. These values can be either “false”, “true” or “{}”. This creates an isolate scope for the directive.
// '@' binding is for passing strings. These strings support {{}} expressions for interpolated values.
// '=' binding is for two-way model binding. The model in parent scope is linked to the model in the directive's isolated scope.
// '&' binding is for passing a method into your directive's scope so that it can be called within your directive.
// The method is pre-bound to the directive's parent scope, and supports arguments.
scope: {
name: "@", // Always use small casing here even if it's a mix of 2-3 words
},
// template replaces the complete element with its text.
template: "<div>Hello {{name}}!</div>",
// compile is called during application initialization. AngularJS calls it once when html page is loaded.
compile: function(element, attributes) {
element.css("border", "1px solid #cccccc");
// linkFunction is linked with each element with scope to get the element specific data.
var linkFunction = function($scope, element, attributes) {
element.html("Name: <b>"+$scope.name +"</b>");
element.css("background-color", "#ff00ff");
};
return linkFunction;
}
};
});
このディレクティブは、Appで次のように使用できます。
<html>
<head>
<title>Angular JS Directives</title>
</head>
<body>
<script src = "//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="directive.js"></script>
<div ng-app = "demoApp">
<!-- Notice we are using Spinal Casing here -->
<demo-directive name="World"></demo-directive>
</div>
</body>
</html>
ディレクティブ定義オブジェクトテンプレート
demoApp.directive('demoDirective', function () {
var directiveDefinitionObject = {
multiElement:
priority:
terminal:
scope: {},
bindToController: {},
controller:
controllerAs:
require:
restrict:
templateNamespace:
template:
templateUrl:
transclude:
compile:
link: function(){}
};
return directiveDefinitionObject;
});
-
multiElement
- trueに設定され、ディレクティブ名の先頭と最後の間の任意のDOMノードが収集され、ディレクティブ要素としてグループ化されます -
priority
- 1つのDOM要素に複数のディレクティブが定義されている場合、ディレクティブを適用する順序を指定できます。数字の大きいディレクティブが最初にコンパイルされます。 -
terminal
- trueに設定され、現在の優先度は実行する指令の最後のセットになります -
scope
- ディレクティブのスコープを設定します。 -
bind to controller
- スコープのプロパティをディレクティブコントローラに直接バインドする -
controller
- コントローラコンストラクタ関数 -
require
- 別の指令を必要とし、そのコントローラをリンク関数の第4引数として注入する -
controllerAs
- ディレクティブスコープ内のコントローラへの参照を指定して、ディレクティブテンプレートからコントローラを参照できるようにします。 -
restrict
- ディレクティブを要素、属性、クラス、またはコメントに制限する -
templateNameSpace
- 指示テンプレートによって使用される文書タイプを設定します:html、svg、またはmath。 htmlはデフォルトです -
template
- デフォルトで、ディレクティブの要素の内容を置き換える、またはtranscludeがtrueの場合はディレクティブ要素の内容をラップするHTMLマークアップ -
templateUrl
-templateUrl
に対して非同期で提供されるurl -
transclude
- ディレクティブが現れる要素の内容を抽出し、ディレクティブが利用できるようにします。コンテンツはコンパイルされ、transclusion関数としてディレクティブに提供されます。 -
compile
- テンプレートDOMを変換する関数 -
link
- コンパイルプロパティが定義されていない場合にのみ使用されます。リンク関数は、DOMリスナの登録とDOMの更新を行います。これは、テンプレートがクローンされた後に実行されます。
基本指令の例
スーパーマンディレクティブ.js
angular.module('myApp', [])
.directive('superman', function() {
return {
// restricts how the directive can be used
restrict: 'E',
templateUrl: 'superman-template.html',
controller: function() {
this.message = "I'm superman!"
},
controllerAs: 'supermanCtrl',
// Executed after Angular's initialization. Use commonly
// for adding event handlers and DOM manipulation
link: function(scope, element, attributes) {
element.on('click', function() {
alert('I am superman!')
});
}
}
});
superman-template.html
<h2>{{supermanCtrl.message}}</h2>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
<script src="superman-directive.js"><script/>
</head>
<body>
<div ng-app="myApp">
<superman></superman>
</div>
</body>
</html>
ディレクティブのrestrict
機能とlink
機能の詳細については、 AngularJSのディレクティブに関する公式ドキュメント
ディレクティブを使ってresuableコンポーネントを作成する方法
AngularJSディレクティブは、AngularJSアプリケーション内のHTMLのレンダリングを制御するものです。 HTML要素、属性、クラス、またはコメントにすることができます。ディレクティブは、DOMを操作するために使用され、HTML要素への新しい動作、データバインディングなどを追加します。角度を指定するディレクティブの例としては、ng-model、ng-hide、ng-ifなどがあります。
同様に、独自のカスタムディレクティブを作成してリジュームすることもできます。カスタムディレクティブのリファレンスを作成します。再利用可能なディレクティブを作成する背後にある意味は、anglejsがangle.jsを使用しているのと同じように、あなたが作成した一連のディレクティブ/コンポーネントを作成することです。これらの再利用可能な指示は、一貫した動作、ルック・アンド・フィールを必要とする一連のアプリケーション/アプリケーションを持つ場合に特に役立ちます。このような再利用可能なコンポーネントの例は、アプリケーション全体または異なるアプリケーションで使用したいが、同じように動作させたり、同じように見せたりするシンプルなツールバーにすることができます。
最初に、あなたのappフォルダにresuableComponentsという名前のフォルダを作り、reusableModuleApp.jsを作ります
reusableModuleApp.js:
(function(){
var reusableModuleApp = angular.module('resuableModuleApp', ['ngSanitize']);
//Remember whatever dependencies you have in here should be injected in the app module where it is intended to be used or it's scripts should be included in your main app
//We will be injecting ng-sanitize
resubaleModuleApp.directive('toolbar', toolbar)
toolbar.$inject=['$sce'];
function toolbar($sce){
return{
restrict :'AE',
//Defining below isolate scope actually provides window for the directive to take data from app that will be using this.
scope : {
value1: '=',
value2: '=',
},
}
template : '<ul> <li><a ng-click="Add()" href="">{{value1}}</a></li> <li><a ng-click="Edit()" href="#">{{value2}}</a></li> </ul> ',
link : function(scope, element, attrs){
//Handle's Add function
scope.Add = function(){
};
//Handle's Edit function
scope.Edit = function(){
};
}
}
});
mainApp.js:
(function(){
var mainApp = angular.module('mainApp', ['reusableModuleApp']); //Inject resuableModuleApp in your application where you want to use toolbar component
mainApp.controller('mainAppController', function($scope){
$scope.value1 = "Add";
$scope.value2 = "Edit";
});
});
index.html:
<!doctype html>
<html ng-app="mainApp">
<head>
<title> Demo Making a reusable component
<head>
<body ng-controller="mainAppController">
<!-- We are providing data to toolbar directive using mainApp'controller -->
<toolbar value1="value1" value2="value2"></toolbar>
<!-- We need to add the dependent js files on both apps here -->
<script src="js/angular.js"></script>
<script src="js/angular-sanitize.js"></script>
<!-- your mainApp.js should be added afterwards --->
<script src="mainApp.js"></script>
<!-- Add your reusable component js files here -->
<script src="resuableComponents/reusableModuleApp.js"></script>
</body>
</html>
ディレクティブはデフォルトで再利用可能なコンポーネントです。個別の角モジュールでディレクティブを作成すると、実際には、異なるangularJsアプリケーション間でエクスポートおよび再利用が可能になります。新しいディレクティブをreusableModuleApp.js内に簡単に追加することができ、reusableModuleAppは独自のコントローラ、サービス、ディレクティブ内にDDOオブジェクトを持ち、動作を定義することができます。
テンプレートと隔離されたスコープを持つ基本ディレクティブ
独立したスコープでカスタムディレクティブを作成すると、ディレクティブ内のスコープが外部スコープから分離され、ディレクティブが誤って親スコープのデータを変更したり、親スコープのプライベートデータを読み込まないように制限します。
分離されたスコープを作成し、カスタムディレクティブが外部スコープと通信できるようにするには、ディレクティブの内部スコープのバインディングを外部スコープにマップする方法を説明するscope
オプションを使用できます。
実際のバインディングは、ディレクティブに付加された追加の属性で行われます。バインド設定は、 scope
オプションと、キーと値のペアを持つオブジェクトで定義されます。
- ディレクティブの分離スコーププロパティに対応するキー
- Angularに、内部のスコープを一致する属性にバインドする方法を示す値
分離スコープのディレクティブの簡単な例:
var ProgressBar = function() {
return {
scope: { // This is how we define an isolated scope
current: '=', // Create a REQUIRED bidirectional binding by using the 'current' attribute
full: '=?maxValue' // Create an OPTIONAL (Note the '?'): bidirectional binding using 'max-value' attribute to the `full` property in our directive isolated scope
}
template: '<div class="progress-back">' +
' <div class="progress-bar"' +
' ng-style="{width: getProgress()}">' +
' </div>' +
'</div>',
link: function(scope, el, attrs) {
if (scope.full === undefined) {
scope.full = 100;
}
scope.getProgress = function() {
return (scope.current / scope.size * 100) + '%';
}
}
}
}
ProgressBar.$inject = [];
angular.module('app').directive('progressBar', ProgressBar);
このディレクティブを使用してコントローラのスコープからディレクティブの内部スコープにデータをバインドする方法の例
コントローラ:
angular.module('app').controller('myCtrl', function($scope) {
$scope.currentProgressValue = 39;
$scope.maxProgressBarValue = 50;
});
表示:
<div ng-controller="myCtrl">
<progress-bar current="currentProgressValue"></progress-bar>
<progress-bar current="currentProgressValue" max-value="maxProgressBarValue"></progress-bar>
</div>
再利用可能なコンポーネントの構築
ディレクティブを使用して、再利用可能なコンポーネントを構築することができます。 「ユーザーボックス」コンポーネントの例を次に示します。
userBox.js
angular.module('simpleDirective', []).directive('userBox', function() {
return {
scope: {
username: '=username',
reputation: '=reputation'
},
templateUrl: '/path/to/app/directives/user-box.html'
};
});
Controller.js
var myApp = angular.module('myApp', ['simpleDirective']);
myApp.controller('Controller', function($scope) {
$scope.user = "John Doe";
$scope.rep = 1250;
$scope.user2 = "Andrew";
$scope.rep2 = 2850;
});
myPage.js
<html lang="en" ng-app="myApp">
<head>
<script src="/path/to/app/angular.min.js"></script>
<script src="/path/to/app/js/controllers/Controller.js"></script>
<script src="/path/to/app/js/directives/userBox.js"></script>
</head>
<body>
<div ng-controller="Controller">
<user-box username="user" reputation="rep"></user-box>
<user-box username="user2" reputation="rep2"></user-box>
</div>
</body>
</html>
user-box.html
<div>{{username}}</div>
<div>{{reputation}} reputation</div>
結果は次のようになります。
John Doe
1250 reputation
Andrew
2850 reputation
ディレクティブデコレータ
場合によっては、指令の追加機能が必要な場合もあります。ディレクティブを書き換え(コピー)する代わりに、ディレクティブの動作を変更することができます。
デコレータは$ inject段階で実行されます。
これを行うには、モジュールに.configを指定してください。このディレクティブはmyDirectiveと呼ばれ、myDirectiveDirectiveを設定する必要があります。 (これは角度を定めたコンベンション[プロバイダーについて読む])。
この例では、ディレクティブのtemplateUrlが変更されます。
angular.module('myApp').config(function($provide){
$provide.decorator('myDirectiveDirective', function($delegate){
var directive = $delegate[0]; // this is the actual delegated, your directive
directive.templateUrl = 'newTemplate.html'; // you change the directive template
return $delegate;
})
});
この例では、クリックされたときにonClickイベントをディレクティブ要素に追加します。これはコンパイルフェーズで発生します。
angular.module('myApp').config(function ($provide) {
$provide.decorator('myDirectiveTwoDirective', function ($delegate) {
var directive = $delegate[0];
var link = directive.link; // this is directive link phase
directive.compile = function () { // change the compile of that directive
return function (scope, element, attrs) {
link.apply(this, arguments); // apply this at the link phase
element.on('click', function(){ // when add an onclick that log hello when the directive is clicked.
console.log('hello!');
});
};
};
return $delegate;
});
});
プロバイダとサービスの両方で同様のアプローチを使用できます。
指令の継承と相互運用性
Angular jsディレクティブはネストするか、相互運用可能にすることができます。
この例では、ディレクティブAdirはBdirがAdirを必要とするため、ディレクティブBdirにコントローラ$ scopeを公開します。
angular.module('myApp',[]).directive('Adir', function () {
return {
restrict: 'AE',
controller: ['$scope', function ($scope) {
$scope.logFn = function (val) {
console.log(val);
}
}]
}
})
require: '^ Adir'を設定してください(角度のドキュメントを見てください。いくつかのバージョンは^文字を必要としません)。
.directive('Bdir', function () {
return {
restrict: 'AE',
require: '^Adir', // Bdir require Adir
link: function (scope, elem, attr, Parent) {
// Parent is Adir but can be an array of required directives.
elem.on('click', function ($event) {
Parent.logFn("Hello!"); // will log "Hello! at parent dir scope
scope.$apply(); // apply to parent scope.
});
}
}
}]);
この方法でディレクティブをネストできます。
<div a-dir><span b-dir></span></div>
<a-dir><b-dir></b-dir> </a-dir>
ディレクティブがHTMLにネストされている必要はありません。