一步一步弄懂angularJS基础

问题1:ng-app指令的使用以及自定义指令

  1. <!doctype html>  
  2. <!--这里的ng-app的属性值就是模块的名称,也就是 angular.module("MyModule", [])中的MyModule-->  
  3. <html ng-app="MyModule">  
  4.     <head>  
  5.         <meta charset="utf-8">  
  6.     </head>  
  7.     <body>  
  8.         <hello></hello>  
  9.       <!--这个标签被完全替换为'<div>Hi everyone!</div>',这一点很重要的-->  
  10.     </body>  
  11.     <script src="js/angular-1.3.0.js"></script>  
  12.     <!--引入指令-->  
  13.     <script src="HelloAngular_Directive.js"></script>  
  14. </html>  
我们看看指令本身的代码是如何定义的
[javascript] view plain copy
  1. var myModule = angular.module("MyModule", []);  
  2. //创建一个模块  
  3. myModule.directive("hello", function() {  
  4.     //这里的指令为hello指令,而且是Element类型,返回的template就是用于替换hello的部分,而replace指定了用template值替换了hello这个标签的内容  
  5.     return {  
  6.         restrict: 'E',  
  7.         template: '<div>Hi everyone!</div>',  
  8.         replace: true  
  9.     }  
  10. });  
问题2:我们来理解一下angularjs的MVC模式
  1. <!doctype html>  
  2. <!--这里的ng-app指令表明下面的所有的指令全部让angularjs处理,只有被具有ng-app属性的DOM元素包含的元素才会受到angularjs的影响-->  
  3. <html ng-app>  
  4.     <head>  
  5.         <meta charset="utf-8">  
  6.     </head>  
  7.     <body>  
  8.     <!--这里指定了一个controller,这个controller是view视图和数据之间的桥梁-->  
  9.         <div ng-controller="HelloAngular">  
  10.          <!--这里指定的视图,也就是用于显示的view-->  
  11.             <p>{{greeting.text}},Angular</p>  
  12.         </div>  
  13.     </body>  
  14.     <script src="js/angular-1.3.0.js"></script>  
  15.     <script src="HelloAngular_MVC.js"></script>  
  16. </html>  
下面是控制器的代码
[javascript] view plain copy
  1. function HelloAngular($scope) {  
  2.     $scope.greeting = {  
  3.         text: 'Hello'  
  4.     };  
  5. }  
问题3:通用controller是通过$scope来完成继承的,但是我们不建议使用通用controller,而是使用服务
  1. <!doctype html>  
  2. <html ng-app>  
  3.     <head>  
  4.         <meta charset="utf-8">  
  5.     </head>  
  6.     <body>  
  7.        <!--这里是CommonController的部分,这个controller指定了内部两个controller共有的逻辑-->  
  8.         <div ng-controller="CommonController">  
  9.         <!--这里是内部第一个controller-->  
  10.             <div ng-controller="Controller1">  
  11.                 <p>{{greeting.text}},Angular</p>  
  12.                 <button ng-click="test1()">test1</button>  
  13.             </div>  
  14.             <!--这里是内部第二个controller-->  
  15.             <div ng-controller="Controller2">  
  16.                 <p>{{greeting.text}},Angular</p>  
  17.                 <button ng-click="test2()">test2</button>  
  18.                 <button ng-click="commonFn()">通用</button>  
  19.             </div>  
  20.         </div>  
  21.     </body>  
  22.     <script src="js/angular-1.3.0.js"></script>  
  23.     <script src="MVC3.js"></script>  
  24. </html>  
下面是三个controller的代码
[javascript] view plain copy
  1. //这个通用的controller指定的是两个controller都具有的方法  
  2. function CommonController($scope){  
  3.     $scope.commonFn=function(){  
  4.         alert("这里是通用功能!");  
  5.     };  
  6. }  
  7. //这里是第一个controller指定了其特有的功能  
  8. function Controller1($scope) {  
  9.     $scope.greeting = {  
  10.         text: 'Hello1'  
  11.     };  
  12.     $scope.test1=function(){  
  13.         alert("test1");  
  14.     };  
  15. }  
  16. //这里是第一个controller指定了其特有的功能  
  17. function Controller2($scope) {  
  18.     $scope.greeting = {  
  19.         text: 'Hello2'  
  20.     };  
  21.     $scope.test2=function(){  
  22.         alert("test2");  
  23.     }  
  24. }  

注意:其实$scope是一个POJO(plain Old JavaScript Object);$scope提供了一些工具方法,如$watch,$apply等;$scope是表达式执行环境,也就是作用域;$scope是树形结构和DOM标签平行;子$scope继承父$scope的所有的属性和方法;每一个angular应用只有一个根$scope,一般位于ng-app上;$scope可以用于传播事件,类似DOM可以往上也可以往下;可以用angular.element($0).scope进行调试。总之,$scope不仅仅是MVC的基础,而且也是双向数据绑定的基础!

问题4:ng-repeat的使用,同时指定了$scope会继承$rootScope,就像原型链一样

  1. <!doctype html>  
  2. <html ng-app>  
  3.     <head>  
  4.         <meta charset="utf-8">  
  5.         <link rel="stylesheet" type="text/css" href="Scope1.css" />  
  6.     </head>  
  7.     <body>  
  8.         <div class="show-scope-demo">  
  9.         <!--这里是第一个controller,是GreetCtrl-->  
  10.             <div ng-controller="GreetCtrl">  
  11.                 Hello {{name}}!  
  12.             </div>  
  13.             <!--这里是第二个controller,是ListCtrl-->  
  14.             <div ng-controller="ListCtrl">  
  15.                 <ol>  
  16.                 <!--ng-repeat指令的使用-->  
  17.                     <li ng-repeat="name in names">  
  18.                         {{name}} from {{department}}  
  19.                     </li>  
  20.                 </ol>  
  21.             </div>  
  22.         </div>  
  23.     </body>  
  24.     <script src="js/angular-1.3.0.js"></script>  
  25.     <script src="Scope1.js"></script>  
  26. </html>  
下面是两个controller,同时注意这时候$scope会继承$rootScope的属性
[javascript] view plain copy
  1. //第一个controller指定了$scope和$rootScope,并且在他们上面都绑定了属性值  
  2. function GreetCtrl($scope, $rootScope) {  
  3.     $scope.name = 'World';  
  4.     $rootScope.department = 'Angular';  
  5. }  
  6. //这里也绑定了$scope属性值  
  7. function ListCtrl($scope) {  
  8.     $scope.names = ['Igor', 'Misko', 'Vojta'];  
  9. }  
问题5:我们来看看$emit和$broadcast用于事件触发的不同
  1. <!doctype html>  
  2. <html ng-app>  
  3.     <head>  
  4.         <meta charset="utf-8">  
  5.         <link rel="stylesheet" type="text/css" href="Scope1.css" />  
  6.     </head>  
  7.     <body>  
  8.     <!--第一个控制器EventController-->  
  9.         <div ng-controller="EventController">  
  10.             Root scope  
  11.             <tt>MyEvent</tt> count: {{count}}  
  12.             <ul>  
  13.                <!--这里是内部的controller,也是通过EventController来控制的-->  
  14.                 <li ng-repeat="i in [1]" ng-controller="EventController">  
  15.                  <!--这里是调用$emit-->  
  16.                     <button ng-click="$emit('MyEvent')">  
  17.                         $emit('MyEvent')  
  18.                     </button>  
  19.                      <!--这里是调用$broadcast-->  
  20.                     <button ng-click="$broadcast('MyEvent')">  
  21.                         $broadcast('MyEvent')  
  22.                     </button>  
  23.                     <br>  
  24.                     Middle scope  
  25.                     <tt>MyEvent</tt> count: {{count}}  
  26.                     <!--EventController-->  
  27.                     <ul>  
  28.                         <li ng-repeat="item in [1, 2]" ng-controller="EventController">  
  29.                             Leaf scope  
  30.                             <tt>MyEvent</tt> count: {{count}}  
  31.                         </li>  
  32.                     </ul>  
  33.                 </li>  
  34.             </ul>  
  35.         </div>  
  36.     </body>  
  37.     <script src="js/angular-1.3.0.js"></script>  
  38.     <script src="Scope2.js"></script>  
  39. </html>  
下面是控制器的代码
[javascript] view plain copy
  1. function EventController($scope) {  
  2.     $scope.count = 0;  
  3.     //这个$scope具有$on方法来监测具体的事件,这里是监测'MyEvent'事件,每次监听到这个事件就把count++  
  4.     $scope.$on('MyEvent', function() {  
  5.         $scope.count++;  
  6.     });  
  7. }  
通过测试我们发现$emit触发事件会导致从同级作用域不断往上传播,但是$broadCast会使得事件从同级作用域不断往下传播。但是不管是$emit还是$broadcast都会在同级作用域之间传播。

问题6:我们看看如何让angularjs实现了站内路由,其本质还是通过hash来完成的

  1. <!doctype html>  
  2. <html ng-app="bookStoreApp">  
  3. <head>  
  4.     <meta charset="UTF-8">  
  5.     <title>BookStore</title>  
  6.     <!--当express直接访问/的时候被重定向到这里,然后静态资源文件如js等都是在public目录下进行加载的-->  
  7.     <script src="1.3.0.14/angular.js"></script>  
  8.     <script src="1.3.0.14/angular-route.js"></script>  
  9.     <script src="1.3.0.14/angular-animate.js"></script>  
  10.     <!--然后加载app.js,这是一个模块,定义了该模块依赖的一些如控制器,过滤器,服务,指令等-->  
  11.     <script src="app.js"></script>  
  12.     <script src="controllers.js"></script>  
  13.     <script src="filters.js"></script>  
  14.     <script src="services.js"></script>  
  15.     <script src="directives.js"></script>  
  16. </head>  
  17. <body>  
  18. <!--这里是视图显示区域-->  
  19.     <div ng-view>  
  20.     </div>  
  21. </body>  
  22. </html>  

我们再来看看app.js中如何指定了依赖模块,同时是如何实现站内路由的。注意:ng-view是由ngRouter模块提供的一个特殊指令,他的独特之处是在HTML中给$router对应的视图内容占位,他会创建自己的作用域并将模版嵌套在内部。ng-view是一个优先级为1000的终极指令,angularjs不会运行同一个元素上的低优先级指令。ngView指令遵循下面的规则:

。每次触发$routeChangeSuccess事件视图都会更新

。如果某个模版和当前的路由相关联:

 (1)创建一个新的作用域;(2)移除上一个视图,同时上一个作用域也会被清除;(3)将新的作用域和当前模版关联在一起;(4)如果路由中有相关的定义,那么就把对应的控制器和当前作用域关联起来;(5)触发$viewContentLoaded事件;(6)如果提供了onload属性,调用该属性指定的函数。

[javascript] view plain copy
  1. //定义了一个模块bookStoreApp,第二个参数是该模块依赖的模块。其中<bookStoreCtrls>是一个模块,其中封装了两个控制器分别为bookStoreCtrls,BookListCtrl  
  2. //其中模块<bookStoreFilters>是一个过滤器  
  3. //模块<bookStoreServices>定义了一个服务  
  4. //模块<bookStoreDirectives>定义了一个指令集合  
  5. var bookStoreApp = angular.module('bookStoreApp', [  
  6.     'ngRoute', 'ngAnimate', 'bookStoreCtrls', 'bookStoreFilters',  
  7.     'bookStoreServices', 'bookStoreDirectives'  
  8. ]);  
  9. //在这个app模块中我们配置了路由,如果是访问了hello就会重定向到http://localhost:3008/hello这个视图文件  
  10. //同时这个视图文件通过HelloCtrl这个控制器进行渲染  
  11. bookStoreApp.config(function($routeProvider) {  
  12.     $routeProvider.when('/hello', {  
  13.         templateUrl: 'http://localhost:3008/hello',  
  14.         controller: 'HelloCtrl'  
  15.     }).when('/list',{  
  16.         //如果是list那么渲染视图http://localhost:3008/bookList,同时渲染工作由BookListCtrl来完成  
  17.         templateUrl:'http://localhost:3008/bookList',  
  18.         controller:'BookListCtrl'  
  19.     }).otherwise({  
  20.         redirectTo: '/hello'  
  21.     })  
  22. });  
很显然是通过模块的config方法来完成的,同时实现了注入$routeProvider对象,最后通过这个对象的when...otherwise方法来实现路由的。记住,上面的ng-view是指定了视图的显示区域。其站内路由还是通过hash来完成的:


我们谈谈ajax:ajax的页面浏览器回退按钮会失效;无法分享页面,也就无法加书签;SEO无法起作用,于是就有了前端路由的观点。前端路由的原理:通过hash值#;HTML5提供的API也可以完成;路由的核心是给应用定义"状态";使用路由会影响应用整体的编码方式(预先定义好状态);考虑兼容性问题与优雅降级。

问题7:我们建议在定义controller时候不是在函数中直接定义,而是在模块中定义:

第一种方式直接在函数中定义:

[javascript] view plain copy
  1. function HelloAngular($scope) {  
  2.     $scope.greeting = {  
  3.         text: 'Hello'  
  4.     };  
  5. }  
第二种方式是首先定义模块,然后在模块中定义控制器,这是我们推荐的方式:
[javascript] view plain copy
  1. var helloModule=angular.module('HelloAngular', []);  
  2. helloModule.controller('helloNgCtrl', ['$scope', function($scope){  
  3.     $scope.greeting = {  
  4.         text: 'Hello'  
  5.     };  
  6. }]);  

我们推荐下面的编码逻辑:


问题7:使用ng-bind防止页面快速刷新或者网速较慢的时候看到源代码{{greeting.text}}

  1. <!doctype html>  
  2. <html ng-app>  
  3.     <head>  
  4.         <meta charset="utf-8">  
  5.     </head>  
  6.     <body>  
  7.            <!--引用一个控制器HelloAngular-->  
  8.         <div ng-controller="HelloAngular">  
  9.         <!--ng-bind用于绑定,之所有使用ng-bind而不是使用{{greeting.text}}是因为在网速慢的时候或者快速刷新的时候会看到{{greeting.text}}这个源代码-->  
  10.             <p><span ng-bind="greeting.text"></span>,Angular</p>  
  11.         </div>  
  12.     </body>  
  13.     <script src="js/angular-1.3.0.js"></script>  
  14.     <script src="HelloAngular_MVC.js"></script>  
  15. </html>  
控制器代码如下(建议用模块来定义):
[javascript] view plain copy
  1. function HelloAngular($scope) {  
  2.     $scope.greeting = {  
  3.         text: 'Hello'  
  4.     };  
  5. }  
问题8:使用ng-class来添加class,而不是使用为$scope添加属性这种方式
  1. <!doctype html>  
  2. <html ng-app="MyCSSModule">  
  3. <head>  
  4.     <meta charset="utf-8">  
  5.     <link rel="stylesheet" href="NgClass.css">  
  6. </head>  
  7. <body>  
  8.     <!--这里是控制器HeaderController,如果isError为true那么添加error类,如果isWarning为true那么谈价类warning就可以了-->  
  9.     <div ng-controller='HeaderController'>  
  10.     <!--这里是视图,其类名和提示信息都是动态添加的,这一点要弄清楚-->  
  11.         <div ng-class='{error: isError, warning: isWarning}'>{{messageText}}</div>  
  12.             <button ng-click='showError()'>Simulate Error</button>  
  13.             <button ng-click='showWarning()'>Simulate Warning</button>  
  14.     </div>  
  15. </body>  
  16. <script src="js/angular-1.3.0.js"></script>  
  17. <script src="NgClass.js"></script>  
  18. </html>  
同时相应的点击事件来修改数据模型中的值,进而使得视图内容能够动态改变,而视图的样式通过ng-class来修改了。除了ng-class另外一种使用细说Angular ng-class
[javascript] view plain copy
  1. var myCSSModule = angular.module('MyCSSModule', []);  
  2. //定义一个模块MyCSSModule  
  3. myCSSModule.controller('HeaderController', ['$scope',  
  4.     //为模块定义个控制器HeaderController  
  5.     function($scope) {  
  6.         $scope.isError = false;  
  7.         $scope.isWarning = false;  
  8.         $scope.showError = function() {  
  9.             $scope.messageText = 'This is an error!';  
  10.             $scope.isError = true;  
  11.             $scope.isWarning = false;  
  12.         };  
  13.         $scope.showWarning = function() {  
  14.             $scope.messageText = 'Just a warning. Please carry on.';  
  15.             $scope.isWarning = true;  
  16.             $scope.isError = false;  
  17.         };  
  18.     }  
  19. ])  
问题8:使用ng-show来控制元素的隐藏和显示
  1. <!doctype html>  
  2. <html ng-app="MyCSSModule">  
  3. <head>  
  4.     <meta charset="utf-8">  
  5. </head>  
  6. <body>  
  7.     <div ng-controller='DeathrayMenuController'>  
  8.     <!--点击的时候就触发toggleMenu逻辑,在该方法里面修改了数据模型的值-->  
  9.         <button ng-click='toggleMenu()'>Toggle Menu</button>  
  10.         <!--ng-show指令通过判断表达式的值进而决定是否应该显示内容-->  
  11.         <ul ng-show='menuState.show'>  
  12.             <li ng-click='stun()'>Stun</li>  
  13.             <li ng-click='disintegrate()'>Disintegrate</li>  
  14.             <li ng-click='erase()'>Erase from history</li>  
  15.         </ul>  
  16.     <div/>  
  17. </body>  
  18. <script src="js/angular-1.3.0.js"></script>  
  19. <script src="NgShow.js"></script>  
  20. </html>  
控制器内部通过非表达式来实现开关效果
[javascript] view plain copy
  1. var myCSSModule = angular.module('MyCSSModule', []);  
  2. //这里定义一个模块<MyCSSModule>,在模块上定义一个控制器DeathrayMenuController  
  3. myCSSModule.controller('DeathrayMenuController', ['$scope',  
  4.     function($scope) {  
  5.         $scope.menuState={show:false};  
  6.         $scope.toggleMenu = function() {  
  7.             //这里的toggleMenu通过非符号'!'就能够动态改变了  
  8.             $scope.menuState.show = !$scope.menuState.show;  
  9.         };  
  10.     }  
  11. ])  

上面演示了如何实现jQuery中的toggleClass效果

问题9:学习使用angular-ui-router使得可以把界面进行分块,也可以参见这个博客学习基础用法

[javascript] view plain copy
  1. var myUIRoute = angular.module('MyUIRoute', ['ui.router', 'ngAnimate']);  
  2. //首先定义一个Module,然后在Module下面通过config来进行配置。同时在函数中注入$stateProvider, $urlRouterProvider两个对象  
  3. myUIRoute.config(function($stateProvider, $urlRouterProvider) {  
  4.     //$urlRouterProvider.when("","/home"); 也就是说$urlRouterProvider通常用于配置非$state额外的路由。下面的otherwise用于指定一个额外的路由  
  5.     $urlRouterProvider.otherwise("/state1");  
  6.     //这里指定了几个状态,分别为state1,state1.list,state2和state2.list  
  7.     $stateProvider  
  8.         .state('state1', {  
  9.             url: "/state1",  
  10.             templateUrl: "http://localhost:3008/state1"  
  11.         })  
  12.         .state('state1.list', {//只有ui-sref可以用于切换状态  
  13.             url: "/list",  
  14.             templateUrl: "http://localhost:3008/state1-list",  
  15.             //这里的templateUrl用于指定了我们的view视图,而controller中定义了我们需要的Model中内容用于view层次进行迭代  
  16.             controller: function($scope) {  
  17.                 $scope.items = ["qinliang", "liangklfang", "fkl", "liangk"];  
  18.             }  
  19.         })  
  20.         .state('state2', {  
  21.             url: "/state2",  
  22.             templateUrl: "http://localhost:3008/state2"  
  23.         })  
  24.         .state('state2.list', {  
  25.             url: "/list",  
  26.             templateUrl: "http://localhost:3008/state2-list",  
  27.             controller: function($scope) {  
  28.                 $scope.things = ["A", "Set", "Of", "Things"];  
  29.             }  
  30.         });  
  31. });  

很显然我们上面的config是挂载在module下面的,学习路由最重要的就是要学会相应的配置是如何反映到浏览器地址栏中的。如下面的这个地址就会看到http://localhost:3008/router3#/index/usermng/highendusers,其前端路由的本质还是通过hash值的改变来完成的!路由的学习要掌握:ui-view,ui-sref等指令的学习,当然也可以深入学习官方文档

问题10:angularjs常见的指令的学习

在生产和部署环境中我们都希望应用加载足够快,以及尽可能做出响应。使用xhr加载模版可能会导致web应用缓慢或者有卡顿的感觉,可以通过把模版包装为javaScript文件然后连同应用程序的其它部分一起传输的方式伪造模版缓存加载,而不是通过xhr提取模版。

第一个:$templateCache使用

  1. <!doctype html>  
  2. <html ng-app="MyModule">  
  3.     <head>  
  4.         <meta charset="utf-8">  
  5.     </head>  
  6.     <body>  
  7.         <hello></hello>  
  8.     </body>  
  9.     <script src="framework/angular-1.3.0.14/angular.js"></script>  
  10.     <script src="$templateCache.js"></script>  
  11. </html>  
我们看看是如何让使用$tempateCache的
[javascript] view plain copy
  1. var myModule = angular.module("MyModule", []);  
  2. //注射器加载完所有模块时,此方法执行一次  
  3. myModule.run(function($templateCache){  
  4.     $templateCache.put("hello.html","<div>Hello everyone!!!!!!</div>");  
  5. });  
  6. //在模版下面挂载指令,指令的名称为hello,所以在页面中可以使用<hello></hello>来指定指令  
  7. myModule.directive("hello", function($templateCache) {  
  8.     return {  
  9.         restrict: 'AECM',//Element,Attribute,Class,Comment  
  10.         template: $templateCache.get("hello.html"),  
  11.         replace: true  
  12.     }  
  13. });  
我们通过$templateCache的get来获取模版,而通过$templateCache的put来设置模版。如果只是为了从$templateCache中获取模版用于页面,那么直接可以使用ng-include指令就可以了
  1. <div ng-include=" 'hello.html' "></div>  
最后的DOM结构为如下:
  1. <div ng-include=" 'hello.html' " class="ng-scope"><div class="ng-scope">Hello everyone!!!!!!</div></div>  
第二个:指令中的link函数

link函数创建可以操作DOM的指令,该函数是可选的。如果定义了编译函数那么他会返回连接函数,因此当两个函数都被定义了,这时候编译函数就会重载链接函数。如果我们的指令很简单并且不需要额外的设置,可以从工厂函数(回调函数)返回一个函数来代替对象。如果这样做了,这个函数就是链接函数。连接函数作用在于:编译模版并且和作用域链接后被调用,因此他负责设置事件监听器,监听数据变化和实时的操作DOM。link函数对绑定了实时数据的DOM具有监控能力,因此需要考虑性能的问题,因此在选择编译函数还是链接函数需要把性能考虑进去

下面是link函数的签名:

[javascript] view plain copy
  1. link:function(scope,element,attrs,SomeController){  
  2.     //在这里可以访问DOM,并且访问在指令中通过require访问的控制器。控制器在所有的指令之间共享,因此指令可以将  
  3.     //控制器当作通信通道(公共API)  
  4. }  
我们下面看一个例子:
  1. <!doctype html>  
  2. <html ng-app="MyModule">  
  3.     <head>  
  4.         <meta charset="utf-8">  
  5.     </head>  
  6.     <body>  
  7.     <!--第一个控制器MyCtrl-->  
  8.         <div ng-controller="MyCtrl">  
  9.             <loader howToLoad="loadData()">滑动加载</loader>  
  10.         </div>  
  11.         <!--第二个控制器MyCtrl2-->  
  12.         <div ng-controller="MyCtrl2">  
  13.             <loader howToLoad="loadData2()">滑动加载</loader>  
  14.         </div>  
  15.     </body>  
  16.     <script src="framework/angular-1.3.0.14/angular.js"></script>  
  17.     <script src="Directive&Controller.js"></script>  
  18. </html>  
我们看看内部如何定义了一个link函数来操作DOM的:
[javascript] view plain copy
  1. var myModule = angular.module("MyModule", []);  
  2. //首先定义一个模块并在模块下挂载控制器,第二个参数为一个数组,其中函数前面的参数都是会被注入到函数形参上面的  
  3. myModule.controller('MyCtrl', ['$scope', function($scope){  
  4.     $scope.loadData=function(){  
  5.         console.log("加载数据中...");  
  6.     }  
  7. }]);  
  8. myModule.controller('MyCtrl2', ['$scope', function($scope){  
  9.     $scope.loadData2=function(){  
  10.         console.log("加载数据中...22222");  
  11.     }  
  12. }]);  
  13. //在模块下挂载一个loader指令  
  14. myModule.directive("loader", function() {  
  15.     return {  
  16.         restrict:"AE",//Element,Attribute  
  17.         link:function(scope,element,attrs){  
  18.             element.bind('mouseenter', function(event) {  
  19.                 //scope.loadData();  
  20.                 // scope.$apply("loadData()");  
  21.                 // 注意这里的坑,howToLoad会被转换成小写的howtoload  
  22.                // scope.$apply(attrs.howtoload);  
  23.                 //其中scope为POJO,但是有一系列的工具方法如$watch,$apply等  
  24.             });  
  25.         }  
  26.     }   
  27. });  
我们看看这个link函数中的参数分别是是什么?

scope参数的签名为:


这scope对象是一个POJO,但是具有$watch,$apply等工具方法,同时这里我们自己定义的loadDate函数也绑定在这个对象上面,其parent属性指向了父scope对象。我们再看看attrs对象的签名:


我们可以看到这个attrs对象的howtoload属性对应的就是我们在这个DOM上面绑定的属性值。而且只能通过这个方法获取到函数'loadData'!还有一个element参数就很显然就是指定的loader元素。深入理解link函数可以阅读angularjs指令中的compile与link函数详解。也可以参考慕课网下面这张图:


第三个:指令中的scope值为{}和true

  1. <!doctype html>  
  2. <html ng-app='MyModule'>  
  3. <head>  
  4.     <meta charset="utf-8">  
  5.     <link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">  
  6. </head>  
  7. <body>  
  8.      <div ng-controller='MainController'>  
  9.           outside myDirective:{{myProperty}}  
  10.           <div my-directive ng-init="myProperty='qinliang'">  
  11.           </div>  
  12.      </div>  
  13. </body>  
  14.    <script src="framework/angular-1.3.0.14/angular.js"></script>  
  15.    <script src='scope.js'></script>  
  16. </html>  
通过把scope设置为{}就会创建一个隔离作用域,这时候指令的模版就无法访问外部作用域了
[javascript] view plain copy
  1. var module=angular.module('MyModule',[]);  
  2. module.controller('MainController',function($scope){})  
  3.        .directive('myDirective',function(){  
  4.         return {  
  5.             restrict:'A',  
  6.             scope:{},//myDirective创建了一个具有隔离作用域的指令,这样做指令的模版就无法访问外部的作用域了  
  7.             priotiry:100,  
  8.             template:'<div>Inside myDirective: {{myProperty}}</div>'  
  9.             //这里的指令的模版就无法访问外部作用域了,也就是{{myProperty}}就没有数据打印出来  
  10.         }  
  11.        })  
下面再来看一个例子:
  1. <!doctype html>  
  2. <html ng-app='MyModule'>  
  3. <head>  
  4.     <meta charset="utf-8">  
  5.     <link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">  
  6. </head>  
  7. <body>  
  8.     <div ng-init='myProperty="qinliang"'>  
  9.         surround scope:{{myProperty}}  
  10.         <div my-inherit-scope-directive></div>  
  11.         <div my-directive></div>  
  12.     </div>  
  13. </body>  
  14.    <script src="framework/angular-1.3.0.14/angular.js"></script>  
  15.    <script src='scope1.js'></script>  
  16. </html>  
下面看看两个指令的输出:
[javascript] view plain copy
  1. var module=angular.module('MyModule',[]).  
  2. directive('myDirective',function(){  
  3.   return {  
  4.        restrict:'A',  
  5.        template:'inside myDirective,isolate scope:{{myProperty}}',  
  6.        scope:{}//指令的模版就无法访问外部作用域了  
  7.   }  
  8. })  
  9. .directive('myInheritScopeDirective',function(){  
  10.    return {  
  11.       restrict:'A',  
  12.       template:'Inside myDirective,isolate sope:{{myProperty}}',  
  13.       scope:true//把scope设置为true,会从父作用域继承并创建一个新的作用域对象  
  14.    }  
  15. })  

my-inherit-scope-directive指令能够正常访问外部作用域的值,而把scope设置为{}就会导致无法获取外部作用域了。通过把scope设置为{}就可以创建隔离作用域,隔离作用域的最主要的使用场景就是创建可以复用的组件,组件可以在未知的上下文中使用,并且可以避免污染所处的外部作用域或者不经意的污染内部作用域。

第四个:自定义指令以及指令之间的依赖

  1. <!doctype html>  
  2. <html ng-app="MyModule">  
  3. <head>  
  4.     <meta charset="utf-8">  
  5.     <link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">  
  6.     <script src="framework/angular-1.3.0.14/angular.js"></script>  
  7.     <script src="Directive&Directive.js"></script>  
  8. </head>  
  9. <body>  
  10.     <div class="row">  
  11.         <div class="col-md-3">  
  12.         <!--自定义指令和属性,ng会处理这些自定义的指令,superman和strength都是指令都会被ng处理-->  
  13.             <superman strength>动感超人---力量</superman>  
  14.         </div>  
  15.     </div>  
  16.     <div class="row">  
  17.         <div class="col-md-3">  
  18.             <superman strength speed>动感超人2---力量+敏捷</superman>  
  19.         </div>  
  20.     </div>  
  21.     <div class="row">  
  22.         <div class="col-md-3">  
  23.             <superman strength speed light>动感超人3---力量+敏捷+发光</superman>  
  24.         </div>  
  25.     </div>  
  26. </body>  
  27.   
  28. </html>  
下面是指令的定义:
[javascript] view plain copy
  1. var myModule = angular.module("MyModule", []);  
  2. //在模块上面定义了一个自定义的指令superman  
  3. myModule.directive("superman", function() {  
  4.     return {  
  5.         scope: {},  
  6.         //scope参数是可选的,可以被设置为true或者一个对象,默认为false。如果设置为true,会从父作用域继承并且创建一个新的作用域对象  
  7.         //如果一个元素上有多个指令试用了隔离作用域,中有一个可以生效。内置指令ng-controller的作用就是从父级作用域继承并创建一个新的作用域  
  8.         //他会创建一个新的从父作用域继承而来的自作用域!这里把scope设置为{}就是创建了一个隔离作用域,这样指令的模版就无法访问外部作用域了  
  9.         restrict: 'AE',  
  10.         controller: function($scope,$element,$attrs,$transclude) {  
  11.           //这里的this就是Constructor {},表示当前的控制器函数本身  
  12.             $scope.abilities = [];  
  13.             this.addStrength = function() {  
  14.                 $scope.abilities.push("strength");  
  15.             };  
  16.             this.addSpeed = function() {  
  17.                 $scope.abilities.push("speed");  
  18.             };  
  19.             this.addLight = function() {  
  20.                 $scope.abilities.push("light");  
  21.             };  
  22.         },  
  23.         link: function(scope, element, attrs) {  
  24.             element.addClass('btn btn-primary');  
  25.             //为元素添加class类名并且为元素绑定了mouseenter事件,当鼠标进入到指定的元素的时候会打印相应的数组集合  
  26.             element.bind("mouseenter", function() {  
  27.                 console.log(scope.abilities);  
  28.             });  
  29.         }  
  30.     }  
  31. });  
  32. myModule.directive("strength", function() {  
  33.     return {  
  34.         require: '^superman',  
  35.         //require设置为字符串代表另外一个指令的名字,require会将控制器注入到其值所指定的指令中,并作为当前指令的链接函数的第四个参数  
  36.         link: function(scope, element, attrs, supermanCtrl) {  
  37.             supermanCtrl.addStrength();  
  38.         }  
  39.     }  
  40. });  
  41. myModule.directive("speed", function() {  
  42.     return {  
  43.         require: '^superman',  
  44.         //其中^表示:指令会在上游的指令链中查找require参数指定的控制器,?表示如果当前指令没有查到指定的控制器就返回null  
  45.         link: function(scope, element, attrs, supermanCtrl) {  
  46.             supermanCtrl.addSpeed();  
  47.         }  
  48.     }  
  49. });  
  50. myModule.directive("light", function() {  
  51.     return {  
  52.         require: '^superman',  
  53.         link: function(scope, element, attrs, supermanCtrl) {  
  54.             supermanCtrl.addLight();  
  55.         }  
  56.     }  
  57. });  
注意:这里要好好理解指令定义的时候的require选项(字符串/数组),link函数(也就是post-link函数),指令定义时候的controller中的this作用!

参考文献:

深入理解ng里的scope

posted @   brave-sailor  阅读(246)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
历史上的今天:
2015-03-12 Android SQLite 事务处理
2015-03-12 Android getReadableDatabase() 和 getWritableDatabase()
2015-03-12 高效的SQLSERVER分页查询(推荐)
点击右上角即可分享
微信分享提示