angular学习笔记(1)
由于之前学习了vue,所以学习angular感觉还蛮顺的,因为这两个框架有很多相似之处,例如:
1. 输出都可以用双括号{{}}
2. 都有双向数据绑定
3. 指令也有很多相似的地方,这里就不一一列举了
下面准备开始,在开始之前先说明:下面代码的html文件的文档声明,还有头标签、文件的引入都省略了,直接从ng-app的内部开始写代码,js文件的模块声明如果没有特殊情况会默认使用var myApp=angular.module("myApp",[]);
angular的模块化
在学习angular听到最多的就是模块化,任何一个功能都是模块。
首先,要想用angular就得先引入angular.js文件,然后在.html文件中标签加入ng-app属性,(一般加在html标签或者body后面加,作为根部作用域),告诉angular下面这块内容由angular管,同时自动启动angular,例如以下代码:
<html ng-app="myApp">
<!--这里是angular的作用范围-->
</html>
然后在自己的.js文件中声明一个变量,用于储存angular返回的模块:
var myApp=angular.module("myApp",[]);
括号中的myApp是标签中ng-app的属性值"myApp",后面的中括号是模块的依赖项,第三个参数是一个回调函数,用来定义angular的一些服务。
如果在html文件里面如果没有写ng-app,这个时候就得在js文件里手动启动angular:
angular.element(document).ready(function(){
angular.bootstrap(document,"myApp");
});
同一个ng-app不能启动两次,如果启动两次会报错!
注意:一般一个应用中只能有一个ng-app,当然有少数情况也会有多个ng-app,但是ng-app不能嵌套在ng-app中,这个时候第二个以后的ng-app需要手动启动,不过一般不建议有多个ng-app。
控制器
一个应用中可以有多个控制器,一般一个控制器用于管理应用中的一个功能,例如:
HTML:
<div ng-controller="controller1">
<!--这里是一些指令-->
</div>
<div ng-controller="controller2">
<!--这里是一些指令-->
</div>
JS:
myApp.controller("controller1",["$scope",function($scope){
//这里是一些逻辑
}]);
myApp.controller("controller2",["$scope",function($scope){
//这里是一些逻辑
}]);
js中的myApp就是上面angular.module("myApp",[])返回的模块,调用controller方法创建一个控制器,第一个参数是HTML中ng-controller的属性值,第二个参数是一个数组,里面包含了控制器的一些依赖还有回调函数,$scope是angular的作用域,一般用于存储各种变量。
在angular中还有一个$rootScope,它是angular的根作用域链,类似于js中的全局作用域,定义在$rootScrop上的变量所有控制器都可以访问。
注意:
1. 不要去复用controller,一个控制器一般只负责一块视图
2. 不要再controller中操作DOM,这不是控制器的职责
3. 不要再controller里面做数据格式化
4. 不要再controller里面做数据操作
5. 一般来说,controller不会互相调用,控制器之间的交互只会通过事件来进行
双向数据绑定
当年angular火有一部分原因是因为它实现了数据的双向绑定,这是其他框架都没有实现的。
所谓的双向数据绑定就是:数据模型里面的值变了,视图也会跟着变,反过来也是,像下面:
HTML:
<div ng-controller="controller">
<input type="text" ng-model="name">
<div>{{name}}<div>
</div>
JS:
myApp.controller("controller",["$scope",function($scope){
$scope.name="张三";
}]);
运行以上代码在页面中就会看见有一个输入框里面的值是张三,下面有一段字也是张三。当我们改变输入框中的值会发现下面的那段字也会变,这就实现了数据的双向绑定。
指令
angular还有一个吸引人的地方就是指令系统。angular内置了很多的指令,同时,还可以让我们自定义指令。前面说到的ng-app和ng-controller就是指令。
ng-bind
HTML:
<div ng-bind="name"></div>
JS:
myApp.controller("controller",["$scope",function($scope){
$scope.name="张三";
}]);
在页面中也会输出张三,和双花括号的作用一样,但是使用ng-bind的好处就是,当页面加载速度慢的时候页面中不会出现双花括号,保证了页面的美观性。
ng-class
HTML:
<div ng-class="my-style:true"></div>
ng-class一般用于控制class是否使用,如果:后面的表达式为true,就使用:前面的类名。
ng-show和ng-hide
HTML:
<div ng-show="true">我是显示的</div>
<div ng-hide="true">我是隐藏的</div>
当ng-show后面的变量为true时,这个html元素就会显示,为false时就回隐藏,ng-hide刚好相反。
ng-repeat
HTML:
<div ng-controller="myController">
<ul>
<li ng-repeat="data in datas">{{data}}</li>
</ul>
</div>
JS:
myApp.controller("myController",["$scope",function($scope){
$scope.datas=["张三","李四","王五"];
}]);
这个时候页面就会输出:
1. 张三
2. 李四
3. 王五
ng-repeat就是用来遍历数据,并且把数据在页面中全部渲染出来,非常方便。
ng-style
HTML:
<div ng-style="{'color':'red','background-color':'green'}">字的颜色石红的,背景是绿的</div>
ng-class-even和ng-class-odd
HTML:
<div ng-controller="myController">
<ul>
<li ng-class-even="'even-style'" ng-class-odd="'odd-style'" ng-repeat="data in datas">{{data}}</li>
</ul>
</div>
JS:
myApp.controller("myController",["$scope",function($scope){
$scope.datas=["张三","李四","王五"];
}]);
这样当li为奇数时,就会用even-style的class类,当li为偶数时就会用odd-style的class类。
ng-click
HTML:
<div ng-controller="myController">
<button ng-click="changeStatus()">点我</button>
</div>
<p>{{status}}</p>
JS:
myApp.controller("myController",["$scope",function($scope){
$scope.status=false;
$scope.changeStatus=function(){
$scope.status=!$scope.status;
}
}]);
每点一次按钮,changeStatus函数就会执行一次,stauts的值就会改变一次
ng-switch
HTML:
<div ng-controller="myController">
<button ng-click="changeStatus()">点我</button>
<ol switch="status">
<li ng-switch-when="true">
当值为true才会显示
</li>
<li ng-switch-when="false">
当值为false才会显示
</li>
</ol>
</div>
<p>{{status}}</p>
JS:
myApp.controller("myController",["$scope",function($scope){
$scope.status=false;
$scope.changeStatus=function(){
$scope.status=!$scope.status;
}
}]);
ng-init
HTML:
<div ng-init="[firstName='张',lastName='三']">
{{firstName+lastName}}
</div>
这是页面就会输出"张三"。ng-init用于初始化变量,不过初始化变量一般不这么做。
ng-src
HTML:
<img ng-src={{imgUrl}} alt="图片">
ng-src是用于解决 src属性用ng表达式bug的一个指令,如果地址中包含ng表达式,用ng-src比较好。
自定义指令
在angular内置的这些指令中肯定是不够用的,所以,angualr可以给我们自定义指令:
HTML:
<hello-world>oldValue</hello-world>
<div class="hello-world">oldValue</div>
<!-- directive:hello-world -->
<div>oldValue</div>
<div hello-world>oldValue</div>
JS:
myApp.directive("helloWorld",[function(){
return{
restrict: 'ECMA',
template:"<div>newValue<span ng-transclude></span></div>",
replace:true,
transclude:true,
link:function(){
//在这里可以操作DOM、给元素绑定事件
}
}
}]);
js部分有个restrict这个是匹配模式,用于匹配不同的属指令创建方式。
E代表匹配元素模式,也就是我们说的html标签<hello-world>oldValue</hello-world>,当需要创建带有自己的模板的指令时,使用这种方法。
C匹配class模式,就是上面的<div class="hello-world">oldValue</div>
M代表匹配注释模式,就是上面的<!-- directive:hello-world --> <div>oldValue</div>,这里需要注意的就是注释的开头和结尾要有个空格,不然angular是识别不出的。
A代表属性模式,即<div hello-world>oldValue</div>,当要为已有的HTML标签增加功能时,使用这种方法创建指令,这也是angular默认的匹配方式。
template是填充到标签里的内容
template里可以看到有个ng-transclude指令,这个指令是在下面的replace和transclude值为true时才有用。当replace为真时原来的oldValue会被替换成template里的内容,但是我们有时候希望保留oldValue,所以把transclude设为true,这个时候angular就知道要把oldValue保存下来。但是保留下来得有地方放,这个时候在template里面加个ng-transclude指令,就可以把oldValue放进去。
template可以换成templateUrl,后面接的是其他的html文件的地址。
link函数可以操作DOM、绑定事件、绑定作用域。
最终代码执行完后会得到(以<hello-world>oldValue</hello-world>的结果为例):
<div>
newValue
<span ng-transclude=""></span>
<div>oldValue</div>
</div>
自定义指令的controller和controllAs
还是上面的代码,将JS部分改为:
var myApp = angular.module("myApp", []);
myApp.directive("helloWorld", [function () {
return {
restrict: 'ECMA',
template: "<div>newValue<span ng-transclude></span></div>",
replace: true,
transclude: true,
controller:function($scope){
console.log($scope);
},
link: function () {
//在这里可以操作DOM、给元素绑定事件
}
}
}]);
myApp.controller('myController', ['$scope', function ($scope) {
console.log($scope);
}]);
这样在控制台输出的两个$scope实际上是同一个$scope。上面那个controller写在那里,其他指令可以通过require属性获得这个controller里面的东西,实现多个指令通过依赖注入进行通信。
require
require可以将其他指令传递给自己,有三个用法:
1. 通过驼峰法(directiveName)的命名指定了控制器应该带有哪一条指令,默认从同一个元素上的指令找
2. ^directiveName,在父级查找
3. ?directiveName,指令可选,找不到不要抛出异常
现在,上面的代码改成了:
myApp.directive("helloWorld", [function () {
return {
restrict: 'ECMA',
template: "<div>newValue<span ng-transclude></span><btn></btn></div>",
replace: true,
transclude: true,
controller:function($scope){
this.fun=function(){
alert("s");
}
},
controllerAs:"test",
link: function (scope,element,attr,test) {
element.on("click",test.fun);
}
}
}]);
myApp.directive('btn', [function () {
return {
restrict: 'E',
require:"^helloWorld",
replace:true,
template:"<button>点我</button>",
link: function (scope, element, attrs,test) {
element.on("click",test.fun);
}
};
}]);
点击按钮会发现弹出两次a。那是因为在helloWorld指令里的link函数调用了一次this.fun下面的btn也调用了一次。如果点击文字就只会弹出一次。
这里的require用了^是因为helloWorld是btn的父级。
scope
scope:true
在上面说了自定义指令里的controller和下面的myController是同一个控制器。但是当我在自定义指令里(helloWorld指令)加一句scope:true时,这两个控制器就有不同的作用域了:
myApp.directive("helloWorld", [function () {
return {
restrict: 'ECMA',
template: "<div>newValue<span ng-transclude></span><btn></btn></div>",
replace: true,
scope:true,
transclude: true,
controller:function($scope){
console.log($scope);
this.fun=function(){
alert("s");
}
},
controllerAs:"test"
}
}]);
这时,控制台输出的两个scope,展开来看会发现它们的id不一样,id小的那个是id大的父作用域,子作用域可以继承父作用域里的属性,父作用域读不到子作用域里的属性。
scope:{}
当scope:{}会创建一个独立作用域,有父元素,但是继承不到父元素的属性。
这个对象有三个参数:
1 .&:把父作用域包装成一个函数,从而以函数的方式读写父作用域的属性
2 .=:作用域的属性与父作用域的属性双向绑定
3 .@:只能读取父作用域里的值
HTML:
<div ng-controller="myController">
<div hello-world a-data="data" b-data="data" data="{{data}}">oldValue</div>
{{data}}
</div>
JS:
myApp.directive("helloWorld", [function () {
return {
restrict: 'ECMA',
template: "<div>newValue<span ng-transclude></span><btn></btn></div>",
replace: true,
scope:{
a:"&aData",
b:"=bData",
data:"@"
},
transclude: true,
controller:function($scope){
console.log($scope.a());
console.log($scope.b);
console.log($scope.data);
this.fun=function(){
$scope.$apply(function(){
$scope.b="aaaa";
});
}
},
controllerAs:"test"
}
}]);
在html中我加了三个属性分别为:a-data,b-data,data分别对应着JS中的a,b,data。第三个是简写形式,如果名字和属性名相同,则后面的名字可以不用写。
当按下按钮会发现页面上的data变为aaaa这就说明b是双向绑定的
tips:@不能读到对象;使用@时html标签里的属性值需要用双花括号括起来。
小补充:priority是用来设置指令的权值,也就是指令执行的顺序。terminal设置是否以当前设置的priority为界限,如果小于设置的priority就不执行。
就到这里,打完收工!