如何将lambda表达式传递给AngularJS指令

时间:2014-04-28 19:46:50

标签: angularjs lambda directive

我正在尝试创建一组AngularJS指令,这些指令将处理对象数组并使用对象本身或者每个实例的属性或子属性执行特定操作。

例如,如果数组包含字符串,则一个这样的指令可能会呈现这些字符串的逗号分隔列表。我期望使用这样的指令:

<csv-list items="myArray" />

但是,如上所述,我希望实现足够灵活,可以将一个对象数组传递给该指令,从而可以指示该指令对每个实例的特定属性或子属性进行操作。如果我可以将lambda表达式传递给指令,我会想象使用它是这样的:

<csv-list items="myArray" member="element => element.name" />

我猜有一个推荐的AngularJS模式来解决这些问题,但我对AngularJS很新,所以我还没有找到它。任何建议将不胜感激。

谢谢, 添

3 个答案:

答案 0 :(得分:1)

您的指令可以查看其他属性,因此您可以添加property-name属性并让您的指令手动检查该属性。您可以使用像$parse这样的ng-repeat来解析表达式。

<csv-list items="element in myArray" member="element.name">

另一种方法是创建一个'属性'过滤器,它接受一个对象数组并从该对象返回一个属性值数组,你可以这样使用:

<csv-list items="myArray|property:name">

答案 1 :(得分:1)

有几种方法可以做到这一点,使用$parse服务可能是最简单的

var parser = $parse("name");
var element = {name:"thingA"};
var x = parser(element);
console.log(x); // "thingA"

Parse已经过优化,可以在这些场景中快速采取行动(单一属性查找)。你可以保持相同的“解析器”功能,并在每个元素上调用它。

你也可以拆分'。'并自己进行简单的查找(以字符串形式将'member'作为字符串读入指令),以简单的形式:

var paths = myPath.split('.');
var val = myObj;
for(var i = 0; i < paths.length; i++){
    val = val[paths[i]];
}
return val;

还有各种类似linq的库,它们支持lambda表达式作为字符串(linqjs,fromjs)。如果你有一个胖箭头功能。

答案 2 :(得分:1)

以下是您在语法上要求的内容(Show me the code - plunkr):

member="'e' |lambda: 'e.name'"

你可以用类似的东西来做这件事(我只针对这个问题写了这个,我在我的应用程序中做了什么,如下所述)

app.filter('lambda', [
  '$parse',
    function ($parse) {
        return function (lambdaArgs, lambdaExpression, scope) {
          var parsed = $parse(lambdaExpression);
          var split = lambdaArgs.split(',');
          var result = function () {
            var args = {};
            angular.extend(args, scope || {});
            for (var i = 0; i < arguments.length && i < split.length; i++) {
              args[split[i]] = arguments[i];
            }
            return parsed(args);
          };
          return result;
        }
    }
]);

高级用法:

(x, y, z) => x * y * z + a // a is defined on scope
'x,y,z' |lambda: 'x * y * z + a':this

:this会将范围传递给lambda,因此它也可以在那里看到变量。如果您愿意,也可以传入别名控制器。请注意,您还可以将过滤器粘贴到lambda过滤器的第一个参数中,例如:

('x'|lambda:'x | currency')(123.45) // $123.45 assuming en-US locale

然而,到目前为止,我通过以下方式避免了我的应用程序中的lambda过滤器:

我采取的第一种方法是使用类似lodash的过滤器。

因此,如果我有一个对象数组和你的案例,我想做名字,我可能会这样做:

myArray | pluck:'name'

其中pluck是一个看起来像的过滤器:

angular.module('...', [
]).filter('pluck', [
  function () {
    return function (collection, property) {
      if (collection === undefined) {
        return;
      }

      try {
        return _.pluck(collection, property);
      } catch (e) {
      }
    }
  }
]);

我已实施containseveryfirstkeyslastpluckrange(已使用)例如[] | range:6[0,1,2,3,4,5]somevalues。你可以通过链接它们来做很多事情。在所有情况下。我实际上只是把lodash库包裹起来。


我采用的第二种方法是在控制器内定义函数,在范围内公开它们。

因此,在您的示例中,我的控制器会执行以下操作:

$scope.selectName = function (item) { return item.name };

然后让指令接受表达式 - & - 并将selectName传递给表达式,并将该表达式作为函数在指令中调用。这可能是Angular团队所推荐的,因为视图中的内嵌不容易进行单元测试(这可能是他们没有实现lambdas的原因)。 (我真的不喜欢这个,但是,有时候(就像你的情况一样)它严格来说是一个演示事物 - 不是功能性的东西,应该在E2E /边界测试中测试,而不是单元测试。我不同意每个小东西都应该进行单元测试,因为经常会导致架构过于复杂(imho),而且E2E测试会遇到同样的事情。所以我个人并不推荐这条路线,尽管我认为团队会这样做。)

3

我采取的第三种方法是让有问题的指令接受属性名作为字符串。我有一个orderableList指令就可以做到这一点。