曾经做过一个下钻表格,就是页面一进来显示所有年份的收入,点击某年时会下钻显示该年每个季度的收入,点击某个季度时会下钻显示该季度每个月的收入。可以点击查看示例代码及效果。
这里少不了回退显示,我们没有用路由来实现,而是使用的面包屑来导航。在表格单元格上点击时,会使用当前数据源的 data 属性去渲染下级时间段对应的表格,同时将对应的时间段路径推入数组作为渲染面包屑的数据源。当在面包屑上点击时间段路径时,会根据路径名去整个数据源中去搜索该时间段对应和数据,重新渲染表格。当然实际应用应该从后台动态获取数据的,而且查询参数很多时候跟显示字段一样。比如说 { id: 1001, name: 2014; value: 35400 }
,id
参数能保证查询出来的值的唯一性,而 name
却不一定唯一。这里这了方便演示,用了一个列表数组。
我们的页面上有两上指令,一个 drillTable
指令,一个是 breadcrumb
指令。当在表格单元格上点击时,要将 drillTable
内部的选择值传到外部作用域中,在 breadcrumb
上点击时间段路径时,也要将当前内部的选择值到外部作用域中。
最直接的方法是使用局部作用域属性 &
,将控制器中定义的 $scope
函数(如本实例中的 drillDown
)传递到指令中,这就关系到同一个问题:AngularJS 中指令函数怎样向传递参数。
存储函数引用并调用它
上一个方案有要吐槽的是:如果指令没有很好的文档记录,(或者传递了多个参数),那么就很难在不查看指令的源代码的情况下,弄清楚该参数的命名。另一个方案可以使用的技巧是在指令上定义函数, 而不在函数名之后添加圆括号。 如下代码所示:
<breadcrumb paths="paths" go="go"></breadcrumb>
若要将参数值传递给外部 go
函数, 可以在指令中执行以下操作。找到 go()(path)
相关代码, 查看如何在传递名称参数值的同时调用 go
函数:
<a ng-click="go()(paths[paths.length - 2])">上一级</a>
...
<a class="ellipsis" title="{{path}}" ng-click="go()(path)" ng-if="!$last">{{path}}</a>
这个技巧的工作方式和原因是什么呢?在本例中调用 $scope.go()
获取函数信息, 如控制台输出所示。这是因为在将函数分配给独立作用域属性时没有添加圆括号。
if(pathpath) {
if(path == 'All') {
$scope.paths = ['All'];
} else {
var index = $scope.paths.indexOf(path);
$scope.paths = $scope.paths.slice(0, index + 1);
…
go()(path)中的第二个圆括号表示调用 go
函数并传递 path
参数值。这起来有点怪怪的,但与我认为的方案一相比要好。指令文档仍需要定义可传递给外部函数的参数, 但代码简单多了。
独立作用域属性 - 原理
直接给指令实例函数绑定 method(parameter)
行吗?不行的。这里的 parameter
在外部作用域没定义,因此将返回 undefined
。这是 AngularJS 的一个不小的坑。下面我们将介绍两个小技巧来解决这一问题。
使用字面量对象
下面代码说明如何将字面量对象从指令传递到外部函数:
<td title="{{hasData ? 'Drill Down' : ''}}" ng-click="drillDown({profit: item})">
{{item.name}} {{hasData ? '...' : ''}}
</td>
请注意, 仅仅使用字面量对象是行不通的。在将外部函数分配给指令时, 还必须定义字面量对象的属性(如本实例的 profit
属性)。函数的参数名称要与字面量对象属性名匹配。如下面的代码片段 :
<drill-table class="table"
headers="['No', 'Period', 'Profile']"
source="profits"
drill-down="drillDown(profit)">
</drill-table>
您可以看到, 参数 profit
被添加到 drillDown
函数中,该函数被指定给指令的独立作用域属性。由于参数名称与字面量对象属性名匹配, 一切都按预期方式工作。虽然这种方法不太直观, 但能工作。
&
或 &attr
提供了在父作用域上下文执行表达式的方法。如果您对运行此过程的原理感兴趣, 可以分析一下 AngularJS 源码。从指令内部(如前面的示例中的 go
独立作用域属性) 中调用一个 &
独立作用域函数时, 将执行以下代码(Angular 1.3x 为例):
case '&':
parentGet = $parse(attrs[attrName]);
isolateBindingContext[scopeName] = function(locals) {
return parentGet(scope, locals);
};
break;
代码代码中的 attrName
变量表示本示例中的指令的 go
独立作用域属性。从调用 $parse
返回的 parentGet
函数如下代码所示:
return function $parseFunctionCall(scope, locals) {
var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
var fn = fnGetter(scope, locals, context) || noop;
if (args) {
var i = argsFn.length;
while (i--) {
args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
}
}
ensureSafeObject(context, expressionText);
ensureSafeFunction(fn, expressionText);
// IE doesn't have apply for some native functions
var v = fn.apply
? fn.apply(context, args)
: fn(args[0], args[1], args[2], args[3], args[4]);
if (args) {
// Free-up the memory (arguments of the last function call).
args.length = 0;
}
return ensureSafeObject(v, expressionText);
};
该处理代码将对象字面量属性映射到外部函数参数, 然后调用该函数。不知道不没有清楚了一些了呢。
参考
- 访问 codepen 查看示例代码及效果。
- Compile cope
评论 (0)