【转】angular指令入坑
独立作用域和函数参数
通过使用本地作用域属性,你可以传递一个外部的函数参数(如定义在控制器$scope中的函数)到指令。这些使用&就可以完成。下面是一个例子,定义一个叫做add的本地作用域属性用来保存传入函数的引用:
angular.module('directivesModule')
.directive('isolatedScopeWithController', function () {
return {
restrict: 'EA',
scope: {
datasource: '=',
add: '&',
},
controller: function ($scope) {
// ...
$scope.addCustomer = function () {
// 调用外部作用域函数
var name = 'New Customer Added by Directive';
$scope.add();
// 添加新的`customer`到指令作用域
$scope.customers.push({
name: name
});
};
},
template: '<button ng-click="addCustomer()">Change Data</button><ul>
<li ng-repeat="cust in customers">{{ cust.name }}</li></ul>'
};
});
指令的消费者可以通过定义一个add属性的方式传递一个外部的函数到指令。如下:
<div isolated-scope-with-controller datasource="customers" add="addCustomer()"></div>
在这个例子中,函数addCustomer()将会在用户点击指令中创建的按钮时被调用。没有参数传入,所以这里是一个相对简单的操作。
如何向addCustomer()函数中传递参数呢?例如,假设addCustomer()函数显示在下面的控制器下并且当函数被调用时需要传递一个name参数到指令中:
var app = angular.module('directivesModule', []);
app.controller('CustomersController', ['$scope', function ($scope) {
var counter = 0;
$scope.customer = {
name: 'David',
street: '1234 Anywhere St.'
};
$scope.customers = [];
$scope.addCustomer = function (name) {
counter++;
$scope.customers.push({
name: (name) ? name : 'New Customer' + counter,
street: counter + ' Cedar Point St.'
});
};
$scope.changeData = function () {
counter++;
$scope.customer = {
name: 'James',
street: counter + ' Cedar Point St.'
};
};
}]);
从指令内传递一个参数到外部函数在你了解它的工作方式后会显得特别简单,下面是一般开发者起初可能会尝试的写法:
angular.module('directivesModule')
.directive('isolatedScopeWithController', function () {
return {
restrict: 'EA',
scope: {
datasource: '=',
add: '&',
},
controller: function ($scope) {
...
$scope.addCustomer = function () {
// 调用外部函数,注意这里直接传递了一个 name 参数
var name = 'New Customer Added by Directive';
$scope.add(name);
// 添加新的`customer`
$scope.customers.push({
name: name
});
};
},
template: '<button ng-click="addCustomer()">Change Data</button><ul>' +
'<li ng-repeat="cust in customers">{{ cust.name }}</li></ul>'
};
});
需要注意的是指令的控制器通过调用$scope.add(name)来尝试调用外部函数并传递一个参数过去。这样可以工作吗?实际上在外部函数中输出这个参数得到的却是undefined,这可能让你抓破脑袋都想不通为什么。那么接下来我们该做什么呢?
选择1:使用对象字面量
一种方法是传递一个对象字面量。下面是演示如何把name传递到外部函数中的例子:
angular.module('directivesModule')
.directive('isolatedScopeWithController', function () {
return {
restrict: 'EA',
scope: {
datasource: '=',
add: '&',
},
controller: function ($scope) {
...
$scope.addCustomer = function () {
// 调用外部函数
var name = 'New Customer Added by Directive';
$scope.add({ name: name });
// Add new customer to directive scope
$scope.customers.push({
name: name,
street: counter + ' Main St.'
});
};
},
template: '<button ng-click="addCustomer()">Change Data</button>' +
'<ul><li ng-repeat="cust in customers">{{ cust.name }}</li></ul>'
};
});
需要注意的是$scope.add()方法调用时现在传递了一个对象字面量作为参数。很不幸,这样仍然不能工作!什么原因呢?传递给$scope.add()的对象字面量中定义的name属性在分配给指令时同样也需要在外部函数中被定义。非常重要的一点是,在视图中写的参数名必须要与对象字面量中的名字匹配。下面是一个例子:
<div isolated-scope-with-controller datasource="customers" add="addCustomer(name)"></div>
可以看到在视图中使用指令时,addCustomer()方法添加了个参数name。这个name必须要与指令中调用$scope.add()时传入的对象字面量中的name相匹配。如此一来指令就能正确工作了。
选择2:存储一个函数引用并调用它
上面那种方式的问题在于在使用指令时必须要给函数传递参数而且参数名必须在指令内以对象字面量的形式被定义。如果任何一点不匹配将无法工作。虽然这种方法可以完成需求,但仍然有很多问题。例如如果指令没有完善的使用说明文档就很难知道指令中需要传递的参数名究竟是什么,这时就不得不去翻指令源码查看参数内容了。
另一种可行的方法是在指令上定义一个函数但在函数名后面不加圆括号,如下:
<div isolated-scope-with-controller-passing-parameter2 datasource="customers" add="addCustomer"></div>
为了传递参数到外部的addCustomer函数你需要在指令中做以下事情。把$scope.add()(name)代码放到可被addCustomer调用的方法下面:
angular.module('directivesModule')
.directive('isolatedScopeWithControllerPassingParameter2', function () {
return {
restrict: 'EA',
scope: {
datasource: '=',
add: '&',
},
controller: function ($scope) {
...
$scope.addCustomer = function () {
// 调用外部函数
var name = 'New Customer Added by Directive';
$scope.add()(name);
...
};
},
template: '<button ng-click="addCustomer()">Change Data</button><ul>' +
'<li ng-repeat="cust in customers">{{ cust.name }}</li></ul>'
};
});
为什么这种方法可以工作?这个需要从&的另一个主要作用说起。&在指令中主要的作用是计算表达式,即在控制器调用以&定义的作用域属性时AngularJS会计算出这个表达式的值并返回。例如在视图中输入add="x = 42 + 2",那么在指令中读取$scope.add()时将会返回这个表达式的计算结果(44),任何一个有效的AngularJS的表达式都可以是add属性的值并在读取add属性时被计算。所以当我们在视图中输入不带圆括号的函数add="customers"时,指令中$scope.add()实际返回的是在控制器中定义的函数customers()。所以在指令中调用$scope.add()(name)就相当于调用控制器的customers(name)。
在指令中输出$scope.add()将会得到以下内容(正好验证上面所说):
&背后的运行机制
如果你对&的运行机制感兴趣,当&本地作用域属性被调用(例如上面例子中的a dd本地作用域属性),下面的代码将会执行:
case '&':
parentGet = $parse(attrs[attrName]);
isolateScope[scopeName] = function(locals) {
return parentGet(scope, locals);
};
break;
上面的attrName变量相当于前面例子中指令本地作用域属性中的add。调用$pares返回的parentGet函数 如下:
function (scope, locals) {
var args = [];
var context = contextGetter ? contextGetter(scope, locals) : scope;
for (var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](scope, locals));
}
var fnPtr = fn(scope, locals, context) || noop;
ensureSafeObject(context, parser.text);
ensureSafeObject(fnPtr, parser.text);
// IE stupidity! (IE doesn't have apply for some native functions)
var v = fnPtr.apply
? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]);
return ensureSafeObject(v, parser.text);
}
处理代码映射对象字面量属性到外部函数参数并调用函数。
虽然没有必要一定去理解如何使用&本地作用域属性,但是去深入发掘AngularJS在背后做了一些什么总是一件有趣的事情。
结尾
从上面可以看到&的传参过程还是有点困难的。然而一旦学会了如何使用,整个过程其实并不算太难用。
【转】angular指令入坑的更多相关文章
- Angular 从入坑到挖坑 - 组件食用指南
一.Overview angular 入坑记录的笔记第二篇,介绍组件中的相关概念,以及如何在 angular 中通过使用组件来完成系统功能的实现 对应官方文档地址: 显示数据 模板语法 用户输入 组件 ...
- Angular 从入坑到挖坑 - 表单控件概览
一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...
- Angular 从入坑到挖坑 - Router 路由使用入门指北
一.Overview Angular 入坑记录的笔记第五篇,因为一直在加班的缘故拖了有一个多月,主要是介绍在 Angular 中如何配置路由,完成重定向以及参数传递.至于路由守卫.路由懒加载等&quo ...
- Angular 从入坑到挖坑 - 模块简介
一.Overview Angular 入坑记录的笔记第七篇,介绍 Angular 中的模块的相关概念,了解相关的使用场景,以及知晓如何通过特性模块来组织我们的 Angular 应用 对应官方文档地址: ...
- Angular 从入坑到挖坑 - Angular 使用入门
一.Overview angular 入坑记录的笔记第一篇,完成开发环境的搭建,以及如何通过 angular cli 来创建第一个 angular 应用.入坑一个多星期,通过学习官方文档以及手摸手的按 ...
- Angular 从入坑到挖坑 - HTTP 请求概览
一.Overview angular 入坑记录的笔记第四篇,介绍在 angular 中如何通过 HttpClient 类发起 http 请求,从而完成与后端的数据交互. 对应官方文档地址: Angul ...
- Angular 从入坑到挖坑 - 路由守卫连连看
一.Overview Angular 入坑记录的笔记第六篇,介绍 Angular 路由模块中关于路由守卫的相关知识点,了解常用到的路由守卫接口,知道如何通过实现路由守卫接口来实现特定的功能需求,以及实 ...
- web前端入坑第五篇:秒懂Vuejs、Angular、React原理和前端发展历史
秒懂Vuejs.Angular.React原理和前端发展历史 2017-04-07 小北哥哥 前端你别闹 今天来说说 "前端发展历史和框架" 「前端程序发展的历史」 「 不学自知, ...
- vue2.x入坑总结—回顾对比angularJS/React的一统
从感性的角度讲,我是不屑于用VUE,觉得react套件用起来更顺手,但是vue现在越来火,所以也不得入vue(杂烩汤)的坑.vue/anguarJS/React,三者对关系现在就是: https:// ...
随机推荐
- Debian8.3如何设置默认不启动Gnome
最近又光荣的回归了debian的怀抱,其实我就是跟风,现在做传媒平时也用不到多么高深的计算机知识,纯粹自己瞎鼓捣……嘿嘿,我相信有很多像我一样的人,刚刚装上了debian可以在同事面前华丽丽的装了一逼 ...
- Python: with...as...
with open(path, 'r') as f: Soup = BeautifulSoup(f.read(), 'lxml') titles = Soup.select('ul > li & ...
- java WEB开发入门
WEB开发入门 1 进入web JAVASE:标准- standard JAVA桌面程序 GUI SOCKET JAVAEE:企业-浏览器控制 web 2 软件结构 C/S :client ...
- webservices接口 file "/axis2-web/listsingleservice.jsp" not found 问题解决
搞了半天 ,原来是services.xml 配置的某个或者某些service 在代码中不存才.扫描的时候找不到对应的service代码所以就会报错
- Wakez计算与压缩的思考
大部分人认为,比尔盖茨在1981年说过这样一句话:640K ought to be enough for anybody.(640个kB对任何人来说都应该足够了).不过后来比尔盖茨否认自己说过这句话. ...
- wap支付宝接口的问题
今天在支付宝接口开发时,遇到的两个坑 第一个: https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.8nHr4i& ...
- linux学习日记之目录配制
linux目录管理遵循FHS标准,主要目标是希望让使用者可以了解已安装软件通常放置于哪个目录上,所以他们希望独立的软件开发商.操作系统制作者.以及想要维护系统的用户,都遵循FHS的标准.也就是说FHS ...
- 转 centos虚拟机环境的构建。
转自:http://www.cnblogs.com/xiaoluo501395377/archive/2013/03/31/CentOs.html 一.前言 作为一个想从事j2ee后台开发的程序猿,l ...
- 《C#本质论》读书笔记(18)多线程处理
.NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...
- 【Java EE 学习 55】【酒店会员管理系统项目总结】
本酒店会员管理系统使用了SSH框架和传值播客提供的协同OA静态页面. 项目地址:https://github.com/kdyzm/HotelMembersManagement 一.需求分析 酒店会员管 ...