AngularJS 指令怎样传递函数参数

AngularJS 指令怎样传递函数参数

Flying
2016-08-25 / 0 评论 / 108 阅读 / 正在检测是否收录...

曾经做过一个下钻表格,就是页面一进来显示所有年份的收入,点击某年时会下钻显示该年每个季度的收入,点击某个季度时会下钻显示该季度每个月的收入。可以点击查看示例代码及效果

angular-drill-down.svg

这里少不了回退显示,我们没有用路由来实现,而是使用的面包屑来导航。在表格单元格上点击时,会使用当前数据源的 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);
  };

该处理代码将对象字面量属性映射到外部函数参数, 然后调用该函数。不知道不没有清楚了一些了呢。

参考

6

评论 (0)

取消