1 AngularJS 指令
AngularJS 通过被称为指令的新属性来扩展 HTML。
AngularJS 通过内置的指令来为应用添加功能。
AngularJS 允许你自定义指令。
AngularJS 指令是扩展的 HTML 属性,带有前缀 ng-。
ng-app 指令初始化一个 AngularJS 应用程序。
ng-init 指令初始化应用程序数据。
ng-model 指令把元素值(比如输入域的值)绑定到应用程序。
完整的指令内容可以参阅 AngularJS参考手册。
示例
<div ng-app="" ng-init="firstName='John'">
<p>在输入框中尝试输入:</p>
<p>姓名:<input type="text" ng-model="firstName"></p>
<p>你输入的为: {{firstName }}</p>
</div>
2 数据绑定
上面实例中的 {{ firstName }} 表达式是一个 AngularJS 数据绑定表达式。AngularJS 中的数据绑定,同步了 AngularJS 表达式与 AngularJS 数据。{{ firstName }} 是通过 ng-model=“firstName” 进行同步。在下一个实例中,两个文本域是通过两个
ng-model 指令同步的,总价会随着数量和价格改变:
<div ng-app="" ng-init="quantity=1;price=5">
<h2>价格计算器</h2>
数量: <input type="number" ng-model="quantity">
价格: <input type="number" ng-model="price">
<p><b>总价:</b> {{ quantity * price }}</p>
</div>
运行效果如下
3 重复 HTML 元素
3.1 使用 ng-repeat 指令重复 HTML 元素:
<div ng-app="" ng-init="names=['Jani','Hege','Kai']">
<p>使用 ng-repeat 来循环数组</p>
<ul>
<li ng-repeat="x in names">
{{ x }}
</li>
</ul>
</div>
运行效果如下
3.2 使用 ng-repeat 指令重复 HTML 元素:
<div ng-app="" ng-init="names=[
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}]">
<p>循环对象:</p>
<ul>
<li ng-repeat="x in names">
{{ x.name + ', ' + x.country }}
</li>
</ul>
</div>
运行效果
4 创建自定义的指令
除了 AngularJS 内置的指令外,我们还可以创建自定义指令。
你可以使用 .directive 函数来添加自定义的指令。
使用驼峰法来命名一个指令, runoobDirective, 但在使用它时需要以 - 分割, runoob-directive:
<body ng-app="myApp">
<runoob-directive></runoob-directive>
<script>
var app = angular.module("myApp", []);
app.directive("runoobDirective", function() {
return {
template : "<h1>自定义指令!</h1>"
};
});
</script>
</body>
你可以通过以下方式来调用指令:
- 元素名
- 属性
- 类名
- 注释
5 限制使用
你可以限制你的指令只能通过特定的方式来调用。
var app = angular.module("myApp", []);
app.directive("runoobDirective", function() {
return {
restrict : "A",
template : "<h1>自定义指令!</h1>"
};
});
restrict 值可以是以下几种:
- E 作为元素名使用
- A 作为属性使用
- C 作为类名使用
- M 作为注释使用
restrict 默认值为 EA, 即可以通过元素名和属性名来调用指令。
6 scope参数值
scope参数是可选的,默认值为false,可选true、对象{};
false:共享父域
true:继承父域,且新建独立作用域
对象{}:不继承父域,且新建独立作用域
6.1 scope参数false
本质:子域与父域共享作用域。
特点:父域修改parentName的同时,指令绑定的parentName的元素会被刷新。
反之,指令内部parentName被修改时,父域的parentName同样会被刷新。
6.2 scope参数true
本质:子域继承父域,并建立独立作用域。
特点:
-
在指令已声明parentName的情况下,父域parentName变更,指令中parentName不会发生变化。指令在true参数下,建立了的scope,独立并隔离与父控制器的scope。反之,指令中parentName变更,父域也不会发生变化。
-
在指令未声明parentName的情况下,父域的parentName变更,指令中parentName也会刷新这种情况很多时候会被忽略,指令的scope没有声明对象时,其元素绑定的仍然是父域的对象。但,一旦指令中Input变更了,对应的独立scope也会自动声明该绑定对象,这就回到了第1种情况。然而,指令中parentName变更,父域是不会变化的;
-
在指令已声明parentName的情况下,在指令中监听父域parentName的变化无效。但监听子域parentName的变化有效独立子域scope,只能监听自己的,不能监听父域的。但通过 s c o p e . scope. scope.parent可以监听父域。
-
在指令未声明parentName的情况下 ,在指令中监听父域parentName的变化有效。
6.3 scope参数{}
本质:子域不继承父域,并建立独立作用域。
特点:
-
当scope对象为空对象时,无论是父域parentName,还是指令子域parentName发生变更,都不会影响到对方。
原理很清楚,就是指令建立的独立作用域,与父域是完全隔离的。 -
当scope对象为非空对象时,指令会将该对象处理成子域scope的扩展属性。而父域与子域之间传递数据的任务,就是可以通过这块扩展属性完成
@:符号将本地作用域同DOM属性的值进行绑定。指令内部作用域可以使用外部作用域的变量。@ 是单向绑定本地作用域
=:可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。
&:符号可以对父级作用域进行绑定,以便在其中运行函数。
如下元素属性上为驼峰命名,单项绑定要加双花括号{{}},双向无需加;acc-name为自定义指定scope中的属性,{{accName}}中的为父类中的
<runoob-directive acc-name="{{accName}}" user-pass="pass" data-fun="callback()"></runoob-directive>
7 angularjs编译的三个阶段
1 将html转换为DOM;
2 .搜索匹配的directive,按照priority排序(默认优先级是0,ng-repeat为1000),并执行directive上的complie方法;
3 .执行directive上的link方法,该方法主要进行scope绑定及事件绑定。
8 自定义指令的完整参数
// 指令
app.directive("ngDirective", function() {
var obj = {
restrict: "EACM", //E: 一元素的形式在Dom中, A: 属性, C: class中, E: 注解中
priority: "100", // 优先级
terminal: false, // 为true时优先级低于该指令的其他指令则无效
template: "<div>hello world!</div>", // 也可以是一个函数 function(element, attrs)
replace: false, // 默认false, 为true时则不会显示<name></name>标签
templateUrl: "", // 可以使一个代表html文件路径的字符串,也可以是一个函数,大致意思与template一样。
// 在本地开发时,需要运行一个服务器,不然使用templateUrl会报错
scope: false, // 布尔值或者对象,true表示继承父作用域,并创建自己的作用域,改变父亲的值儿子的值会改变;
// false表示继承父作用域,改变父亲的值儿子的值会改变,反之亦然
// {}表示创建一个全新的作用域
// @: 单向绑定; =: 双向绑定; $: 调用父类的函数
transclude: false, // 默认为false, 为true时在指令模板中使用ng-transclude显示指令标签中的内容
controller: function() {}, // 可以是字符串和函数
require: "^?", // 字符串或者数组, 字符串代表另一个指令的名字, 他将作为link函数的第4个参数
compile: function(element, attrs, transclude) {},
link: function(scope, element, attrs, requireController) {}
};
return obj;
});
8.1 require
字符串或者数组,字符串代表另一个指令的名字,它将作为link函数的第四个参数。
假设现在我们要编写两个指令,两个指令的link函数中存在许多重合的方法,这时候我们就可以将这些重复的方法写在第三个指令的controller中,然后在这两个指令中,使用require将第三个指令引入进来,然后我们就可以通过link连接函数的第四个参数引用这些重合的方法了。
另外我们可以在require的参数值加上下面的某个前缀,这回改变查找控制器的行为。
没有前缀 指令会在自身提供的控制器中进行查找,如果找不到任何控制器,则会抛出一个error
? 若在当前指令中没有找到所需的控制器,则会将null传给link链接函数的第四个参数
^ 如果在当前的指令中没有找到所需的控制器,则会查找父元素的控制器
?^ 如果在当前和父元素中都没有找到控制器,则会将null传给link
8.2 compile
cmopile选项可以返回一个对象或者函数,在这里我们可以在指令和实时数据被放到DOM之前进行DOM操作,比如我们可以在这里进行添加或者删除节点的DOM操作。
所以编译函数是负责对模板的DOM进行转换,并且仅仅只会运行一次
- compile函数的语法
compile:function compile(tElement,tAttrs,transclude){
return{
pre:function preLink(scope,iElement,iAttrs,controller){},
post:function postLink(scope,iElement,iAttrs,controller){}
}
}
preLink函数会在编译阶段之后,指令链接到子元素之前执行,类似的,postLink会在指令链接到子元素之后执行。这意味着,为了不破坏绑定过程,如果你需要修改DOM结构,你应该在postLink函数中来做这件事情。
8.3 link
link函数负责将作用域与DOM进行链接
若指令中定义有require选项,则link函数会有第四个参数,代表控制器或者所依赖的指令的控制器。