一、指令基础
当浏览器加载一个包含AngularJS应用的HTML时,我们只需要用内置指令ng-app启动AngularJS应用,并标记出应用的根节点,这个指令需要以属性的形式来使用,因此可以将它写到任何位置,但是写到的开始标签上是最常规的做法:
1.指令的使用方式
<html ng-app="myApp">
html>
任何在这个根元素内部的指令只要能够访问作用域,就可以访问$rootScope。
2.自定义指令
通过AngularJS模块API中的.directive()
方法,我们可以通过传入一个字符串和一个函数来注册一个新指令。其中字符串是这个指令的名字,指令名应该是驼峰命名风格的,函数应该返回
一个对象。directive()方法返回的对象中包含了用来定义和配置指令所需的方法和属性。
angular.module('myApp',[])
.directive('myDirective', function() {
return {
restrict: 'E',
template: '
Click me to go to Google' //为了尽快掌握简单的属性定义,我们只用了restrict和template两个设置项来定义指令
};
});
为了让AngularJS能够调用我们的指令,需要修改指令定义中的restrict设置。这个设置告、诉AngularJS在编译HTML时用哪种声明格式来匹配指令定义。我们可以指定一个或多个格式。可以以元素(E) 、属性(A) 、类(C)或注释(M)的格式来调用指令,建议用属性A(在已有的标签上添加功能)和元素(使用模板的时候)的方式来调用。
AngularJS在页面加载以及调用指令定义后生成的代码, AngularJS把生成后的代码提供给Chrome进行渲染。默认情况下, AngularJS将模板生成的HTML代码嵌套在自定义标签
内部
向指令定义中添加一些新的设置:我们可以将自定义标签从生成的DOM中完全移除掉,
并只留下由模版生成的链接。将replace设置为true就可以实现这个效果:
angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
template: 'Click me to go to Google'
};
});
当AngularJS启动应用时,它会把第一个参数当作一个字符串,并以此字符串为名来注册第二个参数返回的对象。 AngularJS编译器会解析主HTML的DOM中的元素、属性、注释和CSS类名中使用了这个名字的地方,并在这些地方引用对应的指令。当它找到某个已知的指令时,就会在页面中插入指令所对应的DOM元素。
template参数是可选的,必须被设置为以下两种形式之一:
一段HTML文本;
一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个代表模板的字符串。
如果模板字符串中含有多个DOM元素,或者只由一个单独的文本节点构成,那它必须被包含在一个父元素内。换句话说,必须存在一个根DOM元素:
template: '\
<div> <-- single root element -->\
<a href="http://google.com">Click mea>\
<h1>When using two elements, wrap them in a parent elementh1>\
div>\
另外,注意每一行末尾的反斜线,这样AngularJS才能正确解析多行字符串。
templateUrl是可选的参数,可以是以下类型:
一个代表外部HTML文件路径的字符串;
一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个外部HTML文件路径的字符串。
默认情况下,调用指令时会在后台通过Ajax来请求HTML模板文件。有两件事情需要知道。
在本地开发时,需要在后台运行一个本地服务器,用以从文件系统加载HTML模板,否则会导致Cross Origin Request Script(CORS)错误。
模板加载是异步的,意味着编译和链接要暂停,等待模板加载完成。
我们自定义指令是为了复用,肯定会用在不同的controller里面。为了在不同的控制器里面使用自定义指令,需要为指令增加配置项attr,指令怎么根据配置项调用不同控制器的方法?见下第5条:link
3.向指令中传递数据
注意,我们在模板中硬编码了URL和链接文本:
template:'"http://google.com"> Click me to go to Google'
我们的目标是关注指令的公共接口,就像其他任何编程语言一样。实际上,应该将上面的模板转换成可以接受两个变量的形式:一个变量是URL,另一个是链接文本:
template: '{{ myLinkText }}'
在主HTML文档中,可以给指令添加myUrl和myLinkText两个属性,这两个参数会成为指令内部作用域的属性:
<div my-directive my-url="http://google.com"
my-link-text="Click me to go to Google">
div>
有好几种途径可以设置指令内部作用域中属性的值。最简单的方法就是使用由所属控制器提供的已经存在的作用域。如果控制器被移除,或者在控制器的作用域中也定义了一个叫myUrl的属性,AngularJS 允许通过创建新的子作用域或者隔离作用域来解决这个常见问题。
<div my-directive my-url="http://google.com"
my-link-text="Click me to go to Google">div>
angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: 'A',
replace: true,
scope: {
myUrl: '@', //绑定策略
myLinkText: '@' //绑定策略
},
template: '' +'{{myLinkText}}'
};
});
4.缓存
在模板第一次被引用时,它会被载入到模板缓存中以便快速的检索。angular直接通过 $templateCache 服务
5.link 方法:
回顾:
directive中的几个属性:
restrict
E: 表示该directive仅能以element方式使用,即:
A: 表示该directive仅能以attribute方式使用,即:
EA: 表示该directive既能以element方式使用,也能以attribute方式使用
transclude
directive可能接受页面上的其他html内容时才会用到,建议先去掉该参数。有些高阶了。
scope
写上该属性时,就表示这个directive不会从它的controller里继承$scope对象,而是会重新创建一个。
templateUrl
你的directive里的html内容
link
可以简单理解为,当directive被angular 编译后,执行该方法,是用来处理指令内部事务的,包括:给元素绑定事件/数据之类的。
compile在编译前执行,负责把template(包括transclude所引用的)变成一个完整的DOM结构。 link在编译后执行,
负责根据controller和scope里的东东,给compile得到的DOM注册事件、关联数据,或者repeat之。
link里面有三个参数:
第一个参数scope基本上就是上面写的那个scope。
element简单说就是$(‘my-dialog’)
attrs是个map,内容是你这个directive上的所有属性,
require 之后,link就会多一个参数(第四个参数,scope,element,attrs,superCon)
当有两种加载方式时,可以给自定义指令增加属性:
指令内部怎么获取到哪种加载方式呢?attr!直接写属性小写名,不要加括号
那什么时候把逻辑写在controller里,什么时候写在link里呢?
如果想让方法暴露出结果,在外部被调用,则写在controller里
link是处理指令内部的事物的,比如给元素绑定事件、数据
6.指令间的交互
假设现在我们要编写两个指令,两个指令中的link链接函数中(link函数后面会讲)存在有很多重合的方法,
这时候我们就可以将这些重复的方法写在第三个指令的controller中(上面也讲到controller经常用来提供指令间的复用行为)
然后在这两个指令中,require这个拥有controller字段的的指令(第三个指令通过require以及link第四个参数——这里是supermanCtrl):
var myModule = angular.module("MyModule", []);
myModule.directive("superman", function() {
return {
scope: {}, //创建独立作用域
restrict: 'AE',
controller: function($scope) {
$scope.abilities = [];
this.addStrength = function() {
$scope.abilities.push("strength");
};
this.addSpeed = function() {
$scope.abilities.push("speed");
};
this.addLight = function() {
$scope.abilities.push("light");
};
},
link: function(scope, element, attrs) {
element.addClass('btn btn-primary');
element.bind("mouseenter", function() {
console.log(scope.abilities);
});
}
}
});
myModule.directive("strength", function() {
return {
require: '^superman',//把supermanCtrl自动注入到指令中,就可以调用superman控制器中的方法了
link: function(scope, element, attrs, supermanCtrl) {
supermanCtrl.addStrength();
}
}
});
myModule.directive("speed", function() {
return {
require: '^superman',
link: function(scope, element, attrs, supermanCtrl) {
supermanCtrl.addSpeed();
}
}
});
myModule.directive("light", function() {
return {
require: '^superman',
link: function(scope, element, attrs, supermanCtrl) {
supermanCtrl.addLight();
}
}
});
页面代码:
<div class="row">
<div class="col-md-3">
动感超人---力量
div>
div>
<div class="row">
<div class="col-md-3">
动感超人2---力量+敏捷
div>
div>
<div class="row">
<div class="col-md-3">
动感超人3---力量+敏捷+发光
div>
div>
7、创建独立scope
为了使指令相互独立使用,我们应该在return里面创建独立scope:
myModule.directive("superman", function() {
return {
scope: {}, //创建独立作用域
restrict: 'AE',
controller: function($scope) {
}
}
});
angularjs scope的绑定策略:见另一篇博文
二、内置指令
这里只列出几个常用的:
(1)ng-app 定义应用程序的根元素。
声明了ng-app的元素会成为$rootScope
的起点,而$rootScope是作用域链的根,通常声明在
(2)ng-model:将表单控件和当前作用域的属性进行绑定,需要注意绑定的scope的范围(父scope与子scope)
<div ng-app="myApp" ng-controller="myCtrl">
<input ng-model="name"> //input加上ng-model指令后,框架会负责input和name变量的自动同步。
div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.name = "John Doe";
});
script>
我们应该始终用ngModel来绑定$scope
上一个数据模型内的属性,而不是$scope上的属性,这可以避免在作用域或后代作用域中发生属性覆盖。例如:
type="text"
ng-model="modelName.someProperty" />
(3)ng-init:该指令被调用时会初始化内部作用域。
这个指令一般会出现在比较小的应用中,比如demo什么的
<div ng-init="job='fighter'">
I'm a/an {{job}}
div>
(4)ng-controller :用来定义一个控制器对象
内置指令ng-controller的作用是为嵌套在其中的指令创建一个子作用域,避免将所有操作和模型都定义在$rootScope上。用这个指令可以在一个DOM元素上放置控制器。
<div ng-app="myApp" ng-controller="myCtrl">
Full Name: {{firstName + " " + lastName}}
div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
});
script>
(5)ng-form :用来定义一个from,通常是用来验证参数
场景稍微复杂一点,比如一个父表单中有多个子表单,子表单中有3个验证通过时父表单便可以提交。但是,是不可以嵌套的。考虑到这种场景,我们便使用ng-form指令来解决这一问题。
详细说明ng-form:见后
<form name="mainForm" novalidate>
<div ng-form="form1">
姓名:<input type="text" ng-required="true" ng-model="name"/><br>
证件号码:<input type="number" ng-minLength="15" ng-maxLength="18" ng-required="true" ng-model="idnum"/>
</div>
<br>
<div ng-form="form2">
监护人姓名:<input type="text" ng-required="true" ng-model="gname"/><br>
监护人证件号码:<input type="number" ng-minLength="15" ng-maxLength="18" ng-required="true" ng-model="gidnum"/>
</div>
<button ng-disabled="form1.$invalid && form2.$invalid">submit all</button>
</form>
ng-valid (有效的).
ng-invalid (无效的).
type="submit" ng-disabled="mainForm.$invalid" /> //也就是表单的状态为$invalid时禁用提交按钮。
ng-pristine (原始,简介).
ng-dirty (脏的).
ng-submitted (提交的)
下面四个是用到HTML的布尔属性:
(6)ng-disabled :禁用表单输入字段。——布尔
使用ng-disabled可以把disabled属性绑定到以下表单输入字段上:
(text、 checkbox、 radio、 number、 url, email、 submit);
;
;
。
在下面的例子中按钮会一直禁用,直到用户在文本字段中输入内容:
<input type="text" ng-model="someProperty" placeholder="TypetoEnable">
<button ng-model="button" ng-disabled="!someProperty">AButton</button>
(7)ng-readonly :通过表达式返回值true/false将表单输入字段设为只读。——布尔
比如:3秒后变成只读.
"text" ng-readonly="stopTheWorld" value="stop the world after 3s"/>
angular.module('myApp', [])
.run(function($rootScope,$timeout){
$rootScope.stopTheWorld=false;
$timeout(function(){
$rootScope.stopTheWorld = true;
},3000)
})
(8) ng-checked : 这个是给用的
——布尔
<input type="checkbox" ng-checked="someProperty" ng-init="someProperty = true" ng-model="someProperty">
(9) ng-selected : 给里面的
用的
——布尔
<label>
<input type="checkbox" ng-model="isFullStack">
I'm Full Stack Engineer
label>
<select>
<option>Front-Endoption>
<option>Back-Endoption>
<option ng-selected="isFullStack">Full Stack !!!option>
select>
(10)ng-show/ng-hide/ng-if
ng-show 指令在表达式为 true 时显示指定的 HTML 元素,否则隐藏指定的 HTML 元素。
根据表达式显示/隐藏**HTML元素,注意是隐藏,不是从DOM移除(**ng-if才是移除),对于大对象的DOM,可以用它,但如果是小对象的DOM,建议使用ng-if
<div ng-show="1+1 == 2">
1+1=2
div>
如果ng-if中的表达式为false,则对应的元素整个会从DOM中移除
<body ng-app="">
保留 HTML: <input type="checkbox" ng-model="myVar" ng-init="myVar = true">
<div ng-if="myVar">
<h1>Welcomeh1>
<p>Welcome to my home.p>
<hr>
div>
<p>当复选框取消选中时 DIV 元素将移除。p>
<p>当重新选中复选框,DIV 元素会重新显示。p>
body>
(11)ng-change :用来设置input/select等内容发生变化时的事件,这个指令要和ngModel联合起来使用。
<input type="text" ng-model="calc.arg" ng-change="calc.result = calc.arg*2" />
<code>{{ calc.result }}code>
(12) ng-bind : ng-bind的行为和{{}}差不多,只是我们可以用这个指令来避免未渲染导致的闪烁。
(13) ng-repeat : 遍历集合(数组),给每个元素生成模板实例,每个实例的作用域中可以用一些特殊属性
<ul class="list-group">
<li ng-repeat="item in tasks track by $index" class="list-group-item" >{{item}}
ul>
(14) 鼠标、键盘事件:
ng-click :点击事件
ng-keyup :按键松开时执行的代码
ng-keydown : 按下按键时执行的代码,目前的测试是ngKeypress针对系统按键是无效的,而ngKeydown可以
ng-keypress : 按下按键时执行的代码
ng-mousedown()、ng-mouseenter、ng-mouseleave、ng-mousemove、ng-mouseover、ng-mouseup
(鼠标点击执行的顺序:Mousedown、 Mouseup、 Click。
不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件。对应mouseout。只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件。对应mouseleave这样的话,mouseenter子元素不会反复触发事件,否则在IE中经常有闪烁情况发生。)
(15)ng-class : 用作用域中的对象动态改变类样式
(16)ng-href、ng-src : ng-href 指令确保了链接是正常的,即使在 AngularJS 执行代码前点击链接。ng-src 指令确保的 AngularJS 代码执行前不显示图片。即表达式生效前不要加载该资源。如果在 href 的值中有 AngularJS 代码,则需要使用 ng-href 而不是 href。
<a ng-href="string">a>
(17)ng-include:使用ng-include可以加载、编译并包含外部HTML片段到当前的应用中。
要记住,使用ng-include时AngularJS会自动创建一个子作用域。如果你想使用某个特定的作用域,例如ControllerA的作用域,必须在同一个DOM元素上添加ng-controller =”ControllerA”指令,这样当模板加载完成后,不会像往常一样从外部作用域继承并创建一个新的子作用域。
<div ng-include="/myTemplateName.html"
ng-controller="MyController"
ng-init="name = 'World'">
Hello {{ name }}
div>