AngularJS 中除了可以用預設的 宣告式語法(Directive) 之外,也可以自訂自己需要用的 宣告式語法
例如自訂一個 sayhello tag
<div ng-app="BallApp">
<h2>Angular Test</h2>
<sayhello message="Good, ">
Tom
</sayhello>
<sayhello>
Johnson
</sayhello>
</div>
Directive Definition Object
要自訂 directive 必須透過一個回傳物件來定義你的操作
這邊紀錄幾個比較常用的 option
priority
priority 是用來負責控制優先權,決定哪一個語法要先執行,預設是 0 (ng-repeat 的 priority 是 1000)
terminal
如果設為true,在有設priority的多個directive下,執行到此directive之後就會停止
restrict
restrict 有四個選擇可以讓你決定自訂的語法要是什麼型態
- E - 元素(Element name):
<my-directive></my-directive>
- A - 屬性(Attribute) (default):
<div my-directive="exp"></div>
- C - 類別(Class):
<div class="my-directive: exp;"></div>
- M - 註解(Comment):
<!-- directive: my-directive exp -->
template
使用template render html
HTML
<h2>View Render</h2>
<div ng-controller="NameCtrl">
<sayhello/>
</div>
Javascript
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
template: '<div>Hello {{ name }}</div>',
// 也可以使用 templateUrl 指定檔案
// templateUrl: 'test.html'
};
});
replace
若為true則會用template取代原本的HTML元素,若為false會將元素塞到到原本的tag裡面
範例
HTML
<sayhello>Johnson</sayhello>
Javascript
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
template: '<div>Hello, Johnson</div>',
replace: true
};
});
transclude
設為true可以將原本的HTML的內容移到template定義的元素裡
範例
HTML
<sayhello>Johnson</sayhello>
Javascript
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
// 透過 ng-transclude 決定 HTML 內容移入的位置
template: '<div>Hello, <span ng-transclude></span></div>',
transclude: true
};
});
scope
當 scope 為 false 時,會使用原先 Controller 的 scope(default)
<div ng-controller="NameCtrl">
<input type="text" ng-model="name" placeholder="Enter Name"/>
<sayhello></sayhello>
</div>
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
scope: false,
template: '<div xxx-attr="name">Hello, {{ name }}</div>',
replace: true,
link: function(scope, element, attrs) {
scope.name = 'Woody';
}
};
});
當 scope 為 true 時,會建立一個繼承 parent scope 的物件
<div ng-controller="NameCtrl">
<input type="text" ng-model="name" placeholder="Enter Name"/>
<sayhello>
</div>
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
$scope.message = 'Hello';
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
scope: true,
template: '<div xxx-attr="name">Hello, {{ name }}</div>',
replace: true,
link: function(scope, element, attrs) {
scope.name = 'Woody';
}
};
});
當 scope 為物件時,會建立新的 isolate scope
<div ng-controller="NameCtrl">
<input type="text" ng-model="name" placeholder="Enter Name"/>
<sayhello>
</div>
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
// 這種狀況下因為 scope 是另外獨立的 scope, 因此 name 不會有值
scope: {},
template: '<div xxx-attr="{{ name }}">Hello, {{ name }}</div>',
replace: true,
link: function(scope, element, attrs) {
// dosomething
}
};
});
Text Binding
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
scope: {
// 透過 @ 將 Local scope 或 Parent scope 的值存進特定的 HTML Attribute,並當成 Local scope 變數
// localName: @Attribute
messageAttr: '@messageAttr',
// 等同於 localName: @localName
id: '@'
},
template: '<div id="{{ name }}" message-attr="Hi,">{{ messageAttr }} {{ id }}</div>',
replace: true,
link: function(scope, element, attrs) {
// dosomething
}
};
});
Two-way Binding
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
// = 的用法與 @ 相同,但會與 Parent scope 做 Twoway Binding
scope: {
isolateName: '=xxxAttr',
// 當使用 = 找不到對應的 Attribute 時會有NON_ASSIGNABLE_MODEL_EXPRESSION 的 exception,可以使用 ? 避開這個判斷
ggName: '=?ggAttr'
},
// HTML 部份只要填入 parent scope model
template: '<div xxx-attr="name">Hello, {{ isolateName }}</div>',
replace: true,
link: function(scope, element, attrs) {
// 這裡的改變後,NameCtrl 的 name 也會變成 Woody
scope.isolateName = 'Woody';
}
};
});
Method binding
<div ng-controller="NameCtrl">
<input type="text" ng-model="name" placeholder="Enter Name"/>
<button ng-click="alertMessage()">alert</button>
<!-- use NameCtrl method 並傳入自訂參數 -->
<sayhello test="callHome(str)">
</div>
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
$scope.alertMessage = function() {
alert("Message");
};
$scope.callHome = function(str) {
alert(str);
};
});
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
scope: {
// 透過 & binding method
localFn: '&alertMessage',
// binding sayhello tag 中的 test Attribute,並定義傳入參數
test: '&'
},
template: '<div alert-message="alertMessage()">Hello, {{ name }}<button ng-click="localFn()">alert</button><button ng-click="test({str:name})">call home</button></div>',
replace: true,
link: function(scope, element, attrs) {
scope.name = 'Woody';
}
};
});
controller
可以為 Directive 定義一個controller,可透過 Dependency Injection 帶入 $scope、$element、$attrs、$transclude
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
controller: function($scope) {
$scope.message = 'Hello';
},
template: '<div>{{ message }}, Johnson</div>',
replace: true
};
});
require
require 是設定 directive 之間的互動
<sayhello-outer>
<sayhello-inner></sayhello-inner>
</sayhello-outer>
var ballApp = angular.module('BallApp', []);
ballApp.controller('NameCtrl', function($scope) {
$scope.name = 'Johnson';
});
ballApp.directive('sayhelloOuter', function() {
return {
scope: {},
restrict: 'E',
controller: function($scope, $compile, $http) {
// this 代表該 controller
this.show = function(childScope) {
console.log(childScope.message);
}
}
};
});
ballApp.directive('sayhelloInner', function() {
return {
scope: {},
restrict: 'E',
// ^ 會去尋找 parent controller,若找不到會 throws error
// ?^ 與 ^ 相同,但找不到不會 throws error
require: '^sayhelloOuter',
link: function(scope, elem, attrs, controllerInstance) {
scope.message = "Hello Johnson";
controllerInstance.show(scope);
}
};
});
compile
compile 是針對我們自訂的 directive 進行運作上的調整
輸入的參數
function compile(tElement, tAttrs, transclude) { ... }
範例
HTML
<form-input type="button" value="Click"></form-input> <form-input></form-input>
Javascript
ballApp.directive('formInput', function() {
return {
restrict: 'E',
// 將 formInput compile 成 一般 HTML input
compile: function(tElement, tAttrs) {
var type = tAttrs.type || 'text';
var val = tAttrs.value;
var htmlText = '<input type=' + type + ' value=' + val + ' />';
tElement.replaceWith(htmlText);
}
};
});
link
link 是負責處理資料的binding和初始化
這個方法只有在 compile 沒有定義時才可以使用
輸入的參數
function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
範例
ballApp.directive('sayhello', function() {
return {
restrict: 'E',
template: '<div>{{ message }}, {{ name }}</div>',
link: function(scope, element, attrs) {
// 取得 attributes 內容
var str = (attrs.message === undefined) ? ('Hello') : (attrs.message);
scope.message = str;
}
};
});
注意
當 Directive 命名有大小寫時,例如:myDirective,HTML使用時要是 my-directive