原文: www.jianshu.com/p/d55293715…
記得最開始學習ionic混合開發的時候, 朋友們都推薦我先學學angular.js, 然後再看ionic框架的東西; 現在想來也確實, ionic框架基本都是UI相關的知識, 並不難, 而頁面裡面涉及到angular.js的各種邏輯才是真正能讓一個人成為大牛的知識;
選這本書就是因為朋友們都推薦這本, 而且講的還不錯, 最重要的是他們有資源, O(∩_∩)O哈哈~
首先介紹一下本書中關於指令的知識結構:
一. 指令簡介
用過HTML的都知道, 往往在實現一些特殊功能的時候, 原有的HTML標籤並不完全能滿足設計要求, 這時候指令就派上用場了; 指令本質上就是擴充套件一些自定義的HTML元素以實現一些特定的功能;
二. 內建指令
AngularJS事實上也是在原生JS的基礎上進行一些擴充套件和優化, 自然就會包括一些自定義的內建指令; 除了form和a等一些過載了原生的HTML元素, 其他的內建指令通常以ng作為字首; 這樣做的目的也是對原生的一種優化, 比如常用的href指令, 無論裡面的連結是可用還是, 不可用的, 當呼叫href的時候, 就會直接返回撥用結果; 而ng-href指令則是當裡面的表示式呼叫並且返回一個值的時候才開啟, 否則會是一個禁用狀態; 當然既然人家官方用ng這個字首, 你就不要跟人家搶了, 換一個, 大家和諧一點兒;
有網友總結了一下AngularJS的內建指令, 一共有63個;
簡單介紹一下form指令:
<form>
First name:</br>
<input type="text" name="firstname" value="huo">
<br>
Last name:</br>
<input type="text" name="lastname" value="yu">
<br><br>
<input type="submit" value="Submit">
</form>複製程式碼
效果圖:
要注意一下幾點:
- HTML原生的form表單是不能巢狀的, 但Angular封裝後可以巢狀
- Angular為form擴充套件了自動校驗, 防止重複提交等功能
- Angular對input元素的type進行了了擴充套件, 一共提供一下10中型別:
text, number, url, email, radio, checkbox, hidden, button, submit, reset; - Angular為表單內建了4種CSS樣式
ng-valid, ng-invalid, ng-pristine, ng-dirty; - 內建校驗器: require, minlength, maxlength
三. 指令詳解
1. 指令定義
本書中對指令的定義是在特定DOM元素上執行的函式, 指令可以擴充套件這個元素的功能;
在Angular中是通過directive()這個模組來定義指令的;而directive()可以接受兩個引數:
- name
指令的名字 - factory_function(函式)
這個函式定義了指令的行為
在HTML中呼叫就行:angular.application('myApp', []) .directive('myDirective', function() { // 一個指令定義物件 return { // 通過設定項來定義指令, 在這裡進行填寫 }; });複製程式碼
<div my-directive></div>複製程式碼
2. 指令函式介紹
2.1 restrict(字串)
這些選項可以單獨使用, 也可以混合使用;
2.2 優先順序(數值型)
由於考慮到效能問題, ngRepeat被設定成所有內建指令中優先順序最高的; 當然如果兩個元素的優先順序一樣, 會執行先宣告的那個;
2.3 terminal(布林型)
它用來告訴AngularJs停止執行當前元素上比本指令優先順序低的指令, 但當優先順序相同的情況還是會執行;
例如:ngView和ngIf, ngIf的優先順序略高於ngView, 如果ngIf表示式為true, ngView就可以被正常執行; 但如果ngIf的表示式為false, ngView就不會被執行;
2.4 template(字串或函式)
template引數必須被設定成以下兩種形式之一:
- 一段HTML文字
- 一個可以接受兩個引數的的函式, 引數為tElement和tAttrs, 並返回一個代表模板的字串;
2.5 templateUrl(字串或函式)
templateUrl引數可以是以下形式:
- 一個代表外部HTML檔案的路徑字串
- 一個可以接受兩個引數的的函式, 引數為tElement和tAttrs, 並返回一個代表模板的字串;
2.6 replace(布林型)
replace是個可選引數, 預設為false; 當設定為true後
.directive('someDirective', function() {
return {
replace: true
template: '<div>looking for something<div>'
};
});複製程式碼
輸出結果為:
<div>looking for something<div>複製程式碼
2.7 scope引數(布林型或物件)
scope引數有三種取值方式;
2.7.1 false(預設值)
直接使用父scope, 內部並沒有一個新的scope,它和指令以外的程式碼共享同一個scope。
①指令
app.directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '../templates/my_template.html',
scope: false, // 預設值
controller: null
}
});複製程式碼
②指令模板(my_template.html)
<div>
<!--這裡ng-model繫結的input,就是父scope的變數input-->
<p>自定義指令scope:<input type="text" ng-model="input"></p>
<p>結果:{{input}}</p>
</div>複製程式碼
③指令的使用(index.html)
<body ng-app="scopeTest" ng-controller="scopeTestStr">
<p>父scope:<input type="text" ng-model="input"></p>
<!--自定義指令-->
<my-directive></my-directive>
</body>複製程式碼
效果如下:
2.7.2 true(繼承父scope)
當設定為true的時候, 會從父作用域繼承並建立一個新的作用域物件;
參照值為false的情況, 只是將值改為true; 效果如下:
2.7.3 { }
建立一個新的"隔離"scope,但仍可與父scope通訊;
隔離的scope,通常用於建立可複用的指令,也就是它不用管父scope中的model。然而雖然說是“隔離”,但通常我們還是需要讓這個子scope跟父scope中的變數進行繫結。繫結的策略有3種:
①@ 單項繫結
<body ng-app="scopeTest">
<!--外部scope-->
<p>父scope:<input type="text" ng-model="input"></p>
<!--內部隔離scope-->
<my-directive my-text="{{input}}"></my-directive>
<script>
var app = angular.module('scopeTest', []);
app.directive('myDirective', function () {
return {
restrict: 'E',
replace: true,
template: '<p>自定義指令scope:<input type="text" ng-model="myText"></p>',
scope: {
myText: '@'
}
}
});
</script>
</body>複製程式碼
效果圖:
②= 雙向繫結
<body ng-app="scopeTest">
<!--外部scope-->
<p>父scope:<input type="text" ng-model="input"></p>
<!--內部隔離scope-->
<!--注意這裡,因為是雙向繫結,所以這裡不要“{{}}”這個符號-->
<my-directive my-text="input"></my-directive>
<script>
var app = angular.module('scopeTest', []);
app.directive('myDirective', function () {
return {
restrict: 'E',
replace: true,
template: '<p>自定義指令scope:<input type="text" ng-model="myText"></p>',
scope: {
myText: '=' // 這裡改成了雙向繫結
}
}
});
</script>
</body>複製程式碼
效果圖:
③& 內部scope的函式返回值和外部scope繫結
<body ng-app="scopeTest">
<!--外部scope-->
<p>父scope:<input type="text" ng-model="input"></p>
<!--內部隔離scope-->
<!--注意這裡,函式名字也要用 連字元命名法-->
<my-directive get-my-text="input"></my-directive>
<script>
var app = angular.module('scopeTest', []);
app.directive('myDirective', function () {
return {
restrict: 'E',
replace: true,
template: '<p>結果:{{ getMyText() }}</p>',
scope: {
getMyText: '&' // 這裡改成了函式返回值的繫結
}
}
});
</script>
</body>複製程式碼
效果圖:
2.8 transclude(布林型)
預設為false; 只有當你希望建立一個可以包含任意內容的指令的時候才會為true; 程式碼如下所示:
app.directive('myDirective', function() {
return {
restrict: 'AE',
transclude: true,
template: "<div> hellow angular ! <div ng-transclude></div></div>"
}
});複製程式碼
2.9 controller(字串或函式)
指令內部的controller, 暴露一些public方法給內部指令使用;
①字串;
angular.module('myApp', [])
.directive('myDirective', function() {
restrict: 'A', // 始終需要
controller: 'SomeController'
})
// 在應用中其他地方定義該控制器
angular.module('myApp')
.controller('SomeController', function($scope, $element, $attrs, $transclude) {
// 控制器邏輯
});複製程式碼
②函式
可以是指令內部通過匿名建構函式的方式來定義一個內聯的控制器;
angular.module('myApp',[])
.directive('myDirective', function() {
restrict: 'A',
controller:
function($scope, $element, $attrs, $transclude) {
// 控制器邏輯
}
});複製程式碼
現在你也可以知道, 控制器中還有其他一些特殊的服務可以注入到指令中, 這些服務有:
- $scope 指令對應的作用域
- $element 指令對應的元素
- $attrs 當前元素屬性組成的物件
- $transclude 嵌入鏈式函式會與對應的嵌入作用域進行預繫結
在控制器內部操作DOM和angular.js風格相悖的做法, 但通過鏈式函式就可以實現這個需求; 例如在compile引數中使用transcludeFn是推薦的做法;
本文提供了一個新增超連結的例子:
angular.module('myApp')
.directive('link', function() {
return {
restrict: 'EA',
transclude: true,
controller:
function($scope, $element, $transclude, $log) {
$transclude(function(clone) {
var a = angular.element('<a>');
a.attr('href', clone.text());
a.text(clone.text()); 8 $log.info("Created new a tag in link directive");
$element.append(a);
});
}
};
});複製程式碼
link函式可以與控制器指令的控制器互換; 控制器用來提供指令間的複用行為, 但連結函式只能在當前內部指令中定義行為且無法在指令間複用;
link函式可以將指令互相隔離, 而controller則定義可複用的行為;
下面附一張link函式與compile函式的比較圖:
2.10 require(字串或陣列)
字串或陣列的值會是當前指令作用域中使用的指令名稱, 即用來呼叫暴露的內部方法;
對於指令的學習, 書中提供了內部原理, 當然光這些還是遠遠不夠, 附上一個第三方指令庫, 看看原始碼, 體驗一下別人是怎麼寫的;
AngularUI