对最基本的web技术的认识
- HTML文档是一个纯文本文件,包含了页面的结构以及由CSS定义的样式,或者可以操作样式的JavaScript代码。
- HTML节点是嵌套在另一个元素内元素或一串字符。除了文本节点外,所有元素都是节点。
- HTML元素由一个开始标签和一个结束标签组成。
- HTML标签用来标记元素的开始和结束。标签本身用尖括号来声明。- - 属性,用来给HTML元素添加额外的信息。这些属性设置在开始标记中。可以使用形如key=”value”的键值对设置它们,或者只设置键。
自定义HTML元素和属性
基于我们对HTML元素的理解,指令本质上就是angularjs扩展具有自定义功能的HTML元素的途径。
示例:我们可以创建一个自定义元素,它实现了
注意,这个自定义元素使用了特殊的开始和闭合标签my-better-video,以及my-href这个自定义属性。
为了让这个标签更有用,可以将浏览器默认的video标签重载,用下面的这种写法代替它:1
2
3<video my-href="/goofy-video.mp4">
Can stlll take children nodes
</video>
我的第一个指令:1
2
3
4
5
6
7
8
9
10
11<my-directive></my-directive>
假设我们已经创建了一个完整的HTML文档,其中包含了angularjs,并且DOM中已经用ng-app指令标识出了应用的根元素,当angularjs编译HTML时就会调用指令。
调用指令意味着执行指令背后与之相关联的JavaScript代码,这些代码是我们用指令定义写出来的。
myDirective指令的定义看起来是这样的:
angular.module('myApp',[])
.directive('myDirective',function(){
return {
restrict:'E',
template:'<a href="http://www.baidu.com">Click me to go to Google</a>'
};
});
下面向指令定义中添加一些新的设置:我们可以将自定义标签从生成的DOM中完全移除掉,并只留下由模板生成的链接。将replace设置为true就可以实现这个效果:1
2
3
4
5
6
7
8angular.module('myApp',[])
.directive('myDirective',function(){
return {
restrict:'E',
replace:true,
template:'<a href="http://www.baidu.com">Click me to go to baidu</a>
};
});
restrict参数有元素(E),属性(A),类(C)或注释(M)的格式来调用指令。
示例:1
2
3
4
5
6
7
8angular.module('myApp',[])
.directive('myDirective',function(){
return {
restrict:'EAC',
replace:true,
template:'<a href="http://www.baidu.com">Click me to go to Google</a>
};
});
无论有多少种方式可以声明指令,我们坚持使用属性方式,因为它有比较好的跨浏览器兼容性:
表达式
1 | <h1 ng-init="greeting='HelloWorld'"> |
我们将表达式greeting = ‘Hello World’赋值给内置指令ng-init,在表达式中,我们将greeting属性的值设置为Hello World,然后计算花括号内的这个表达式的值。
这两种情况都会在当前作用域中计算一个普通的JavaScript表达式。根据这个表达式的值。
这两种情况都会在当前作用域中计算一个普通的JavaScript表达式。根据这个表达式放置的位置不同,当前作用域可以是angularjs在应用启动时调用ng-app实例化的$rootScope,也可以是某个自作用域,比如某个控制器的作用域。
用表达式来声明指令
我们知道声明指令时既可以使用表达式,也可以不使用表达式,回顾几种合法的表达式声明。1
2
3
4<my-directive="someExpression></my-directive>
<div my-directive="someExpression></div>
<div class="my-directive:someExpression"></div>
<!-- directive:my-directive someExpression -->
这里有一个值得注意的问题,赋值给指令的表达式会在哪个环境运行?要知道这个,首先要了解一个复杂但非常重要的概念,就是当前作用域,它由DOM周围嵌套的控制器提供。
首先快速了解一下由DOM通过内置指令ng-controller提供的作用域。这个指令的作用是在DOM中创建一个新的自作用域:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28<p>we can access:{{rootProperty}}</p>
<div ng-controller="ParentController">
<p>We can access:{{rootProperty}} and {{parentProperty}}</p>
<div ng-controller="childController">
<p>
we can access:
{{rootProperty}} and
{{parentProperty}} and
{{childProperty}}
</p>
<p>{{fullSentenceFromChild}}</p>
</div>
</div>
angular.module('myApp',[])
.run(function($rootScope){
//使用.run访问$rootScope
$rootScope.rootProperty='root scope';
})
.controller('ParentController',function($scope){
//使用.controller访问'ng-controller'内部的属性
//在DOM忽略的$scope中,根据当前控制器进行推断
$scope.parentProperty = 'parent scope';
})
.controller('ChildController',function($scope){
$scope.childProperty = 'child scope';
//同在DOM中一样,我们可以通过当前$scope直接访问原型中的任意属性
$scope.fullSentenceFromChild = 'Same $scope: We can access:' + $scope.rootProperty + 'and'+$scope.parentProperty + 'and '+$scope.childProperty ;
});
angularjs作用域详解
angularjs中,子作用域一般都会通过JavaScript原型继承机制继承其父作用域的属性和方法。但有一个例外:在directive中使用scope:{…},这种方式创建的作用域是一个独立的”Isolate”作用域,它也有父作用域,但父作用域不在原型链上,不会对父作用域进行原型继承。这种方式定义作用域通常用于构造可复用的directive组件。
作用的原型继承是非常简单普遍的,甚至你不必关心它的运作。直到你在子作用域中向父作用域的原始类型属性使用双向数据绑定2-way data bingding,比如Form表单的ng-model为父作用域中的属性,且为原始类型,输入数据后,它不会如你期望的那样运行–Angularjs不会把输入数据写到你期望的父作用域属性中去,而是直接在子作用域创建同名属性并写入数据。这个行为符合JavaScript原型继承机制的行为。包括ng-repeat,ng-switch,ng-view,ng-include都会创建子作用域。
示例1
2
3
4比如:
<input type="text" ng-model="someObj.prop1">
优于:
<input type="text" ng-model="prop1">
向指令中传递数据
回顾一下如何定义指令:1
2
3
4
5
6
7
8angular.module('myApp',[])
.directive('myDirective',function(){
return {
restrict:'A',
replace:true,
template:'<a href="http://www.baidu.com">Click me to go to baidu</a>
}
});
我们在模板中硬编码了URL和链接文本:
如果必将URL和链接文本混在指令内部,可以为其他使用我们指令的人提供更好的体验。我们的目标是关注指令的公共接口,就像其他任何编程语言一样。实际上,应该将上面的模板转换成可以接受两个变量的形式:一个变量是URL,另一个是链接文本:
template:’
在主HTML文档中,可以给指令添加myUrl和myLinkText两个属性,这两个参数会成为指令内部作用域的属性:1
2
3<div my-directive
my-url="http://www.baidu.com" my-link-text="click me go to baidu">
</div>
指令的@绑定策略
我们用属性将数据从DOM中复制到指令的隔离作用域中:1
2
3
4
5
6
7
8
9
10
11
12
13<div my-directive my-url="http://www.baidu.com" my-link-text = "Click me go to baidu"></div>
angular.module('myApp',[])
.directive('myDirective',function(){
return {
restrict:'A',
replace:true,
scope:{
myUrl:'@',//绑定策略
myLinkText:'@'//绑定策略
},
template:'<a href="{{myUrl}}">'+'{{myLinkText}}</a>'
};
});
由于作用域中属性经常是私有的,因此可以指定我们希望将这个内部属性同哪个DOM属性进行绑定:1
2
3
4scope:{
myUrl:'@someAttr',
myLinkText:'@'
}
上面的隔离作用域中的内容是:将指令的私有属性$scope.myUrl同DOM中some-attr属性的值绑定起来。这个值既可以是硬编码的也可以是当前作用域(例如Some-attr=”“)中某个表达式的运算结果。
在DOM中要用some-attr代替my-url:1
2<div my-directive some-attr="http://www.baidu.com" my-link-text="click me go to baidu">
</div>
更进一步,还可以在DOM对应的作用域上运算表达式,并将结果传递给指令,在指令内部最终被绑定在属性上:1
2<div my-directive some-attr="{{'http://'+'www.baidu.com'}}">
</div>
在此之上,我们来看看如何创建一个文本输入域,并将输入值同指令内部隔离作用域的属性绑定起来:1
2
3<input type="text" ng-model="myUrl"/>
<div my-directive some-attr="{{myUrl}}" my-link-text="Click me go to baidu">
</div>
这段代码是可以工作的,但如果我们将文本输入字段移到指令内部并在另一个指令中进行绑定,就无法正常工作了。1
2
3
4<div my-directive some-attr="{{myUrl}}" my-link-text="click me go to baidu">
</div>
还有下面这段代码:
template:'<div><input type="text" ng-model="myUrl"/> <a href="{{myUrl}}">{{myLinkText}}</a></div>
出现这种现象的原因是,内置指令ng-model在它自身内部的隔离作用域和DOM的作用域之间创建了一个双向数据绑定。
让我们来模仿一下这个设置过程以使例子能正常工作,我们的目标是理解双向数据绑定,以及ng-model在这个过程中的行为。
双向数据绑定或许是angularjs中最重要且无法通过jQuery简单实现的功能之一。我们需要自己实现它进而了解它的神奇效果,接下来我们的隔离作用域ng-model内部的隔离作用域之间创建一个双向数据绑定,这样我们的例子就完整了。将内部的$scope.myUrl属性同当前控制器作用域中的theirUrl属性进行绑定,在DOM中通过作用域查询来实现这个绑定。
在这个流程中,给两个方向的绑定都添加一个文本输入字段,通过这两个输入字段可以方便地观察作用域是如何在DOM中通过原型继承链接在一起的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<label>Their URL field:</label>
<input type="text" ng-model="theirUrl">
<div my-directive some-attr="theirUrl" my-link-text="Click me go to baidu"></div>
angular.module('myApp',[])
.directive('myDirective',function(){
return {
restrict:'A',
replace:true,
scope:{
myUrl:'=someAttr',//经过了修改
myLinkText:'@'
},
template:'<div><label>My Url Field:</label><input type="text" ng-model="myUrl"/><a href="{{myUrl}}">{{myLinkText}}</a></div>
};
});
唯一修改的使用=绑定策略代替了@。总地来说,这个例子展示了双向数据绑定的神奇效果,它是angularjs的主要卖点之一。了解内部指令的工作原理非常重要,这样才能在同自定义指令一起使用时把他们的行为考虑在内。