在属性中指定的angularjs指令调用函数并向其传递参数


102

我想创建一个链接到属性的指令。该属性指定应在范围上调用的函数。但我也想将参数传递给链接函数内部确定的函数。

<div my-method='theMethodToBeCalled'></div>

在链接函数中,我绑定到jQuery事件,该事件传递了我需要传递给函数的参数:

app.directive("myMethod",function($parse) {
  restrict:'A',
  link:function(scope,element,attrs) {
     var expressionHandler = $parse(attrs.myMethod);
     $(element).on('theEvent',function( e, rowid ) {
        id = // some function called to determine id based on rowid
        scope.$apply(function() {expressionHandler(id);});
     }
  }
}

app.controller("myController",function($scope) {
   $scope.theMethodToBeCalled = function(id) { alert(id); };
}

在不传递id的情况下,它可以正常工作,但是一旦尝试传递参数,就不再调用该函数


如何通过隔离范围访问对象属性而无需双向绑定?我认为这很有帮助。使用隔离的作用域,并通过$ scope。$ parent
。– JJbang

Answers:


98

马可的解决方案效果很好

与推荐的Angular方式相反(如treeface的plunkr所示),是使用不需要定义expressionHandler的回调表达式。在marko的示例更改中:

在模板中

<div my-method="theMethodToBeCalled(myParam)"></div>

指令链接功能

$(element).click(function( e, rowid ) {
  scope.method({myParam: id});
});

与marko的解决方案相比,这确实有一个缺点-第一次加载时,methodToBeCalled函数将使用myParam === undefined调用。

可以在@treeface Plunker上找到工作样本


确实,在自定义属性中为函数名称提供参数的这种模式似乎是在指令中定义“回调函数”的最简单,最可靠的方法。
rekna 2013年

3
将“ setProduct”称为2种不同的东西非常混乱-属性和作用域函数,这使得很难理解哪一个在哪里。
德米特里·扎伊采夫

令人困惑的是,正确答案为何使用隔离范围,但问题却没有。还是我错过了什么?
j_walker_dev 2014年

根据我在角度1.2.28中使用此代码的测试,效果很好。我的测试在第一次加载时不使用undefined调用theMethodToBeCalled。笨拙的例子也没有。这似乎没有问题。换句话说,当您要将参数传递到指令链接(在隔离范围内)时,这似乎是正确的方法。我建议更新有关缺点和myParam === undefined的注释。
jazeee,2015年

在我的情况下,这种情况“与marko的解决方案相比确实有一个缺点-第一次加载时,将使用myParam === undefined调用theMethodToBeCalled函数”。我想这里有一些隐式上下文
Victor

95

只是为其他答案添加一些信息- &如果您需要隔离的范围,则使用是一种好方法。

marko解决方案的主要缺点是,它迫使您在元素上创建一个隔离范围,但是您只能在元素上创建一个隔离范围(否则会遇到角度错误:多个指令[directive1,directive2]询问用于分离的范围

这意味着您:

  • 不能在本身具有隔离范围的元素帽子上使用它
  • 解决方案不能在同一元素上使用两个指令

由于最初的问题在restrict:'A'两种情况下都使用指令,这两种情况在较大的应用程序中可能经常出现,因此在此处使用孤立的作用域不是一个好习惯,也是不必要的。实际上,rekna在这种情况下有很好的直觉,并且几乎可以正常工作,他唯一做错的就是将$ parsed函数称为错误(请参阅此处返回的内容:https : //docs.angularjs.org/api/ ng / service / $ parse)。

TL; DR; 固定问题代码

<div my-method='theMethodToBeCalled(id)'></div>

和代码

app.directive("myMethod",function($parse) {
  restrict:'A',
  link:function(scope,element,attrs) {
     // here you can parse any attribute (so this could as well be,
     // myDirectiveCallback or multiple ones if you need them )
     var expressionHandler = $parse(attrs.myMethod);
     $(element).on('theEvent',function( e, rowid ) {
        calculatedId = // some function called to determine id based on rowid

        // HERE: call the parsed function correctly (with scope AND params object)
        expressionHandler(scope, {id:calculatedId});
     }
  }
}

app.controller("myController",function($scope) {
   $scope.theMethodToBeCalled = function(id) { alert(id); };
}

11
+1确实-无需隔离范围-更加清洁!
Dmitri Zaitsev 2014年

如果属性仅包含方法名称(例如<div my-method ='theMethodToBeCalled'> </ div>)或内联函数(例如<div my-method ='alert(“ hi”);'> </ div>
乔纳森。

1
如果您在使用这些方法时遇到麻烦,请记住,被调用的方法必须在您传递给从返回的函数的范围内可用$parse
ragamufin 2015年

这个答案正是我想要的+1
grimmdude 2015年

什么是“ theEvent”?如何在子div上执行该功能?
Shlomo

88

不知道确切要做什么...但是仍然有可能的解决方案。

创建一个在本地作用域中具有“&”属性的作用域。它“提供了一种在父作用域的上下文中执行表达式的方法”(有关详细信息,请参见指令文档)。

我还注意到,您使用了快捷链接功能,并将其推入其中的对象属性。你不能那样做。更明确的是(imho)仅返回指令定义对象。请参阅下面的代码。

这是一个代码示例和一个小提琴

<div ng-app="myApp">
<div ng-controller="myController">
    <div my-method='theMethodToBeCalled'>Click me</div>
</div>
</div>

<script>

   var app = angular.module('myApp',[]);

   app.directive("myMethod",function($parse) {
       var directiveDefinitionObject = {
         restrict: 'A',
         scope: { method:'&myMethod' },
         link: function(scope,element,attrs) {
            var expressionHandler = scope.method();
            var id = "123";

            $(element).click(function( e, rowid ) {
               expressionHandler(id);
            });
         }
       };
       return directiveDefinitionObject;
   });

   app.controller("myController",function($scope) {
      $scope.theMethodToBeCalled = function(id) { 
          alert(id); 
      };
   });

</script>

当我在MethodToBeCalled内部设置$ scope.id = id时,该函数将被调用,视图不会更新。可能我需要将expressionHandler(id)包装在scope.apply中。
rekna 2013年

2
太棒了,这帮助我找到了解决问题的方法。值得一提的是,如果您要进行更深层的指令嵌套,则只需要在顶层执行此操作。考虑一下这一点:plnkr.co/edit/s3y67iGL12F2hDER2RNl?p=preview我在其中通过两个指令传递方法。
13年

3
这是第二种方法(也许更具角度):plnkr.co/edit/r9CV9Y1RWRuse4RwFPka?p=preview
treeface

1
没有范围隔离怎么办?
EvanLévesque2014年

3
+1,因为此解决方案告诉我我需要调用scope.method(); 首先要获得该方法的实际参考。
2015年

6

您可以通过使用attrName: "&"引用外部作用域中的表达式来创建使用params执行函数调用的指令。

我们想将ng-click指令替换为ng-click-x

<button ng-click-x="add(a,b)">Add</button>

如果我们有这个范围:

$scope.a = 2;
$scope.b = 2;

$scope.add = function (a, b) {
  $scope.result = parseFloat(a) + parseFloat(b);
}

我们可以这样写指令:

angular.module("ng-click-x", [])

.directive('ngClickX', [function () {

  return {

    scope: {

      // Reference the outer scope
      fn: "&ngClickX",

    },

    restrict: "A",

    link: function(scope, elem) {

      function callFn () {
        scope.$apply(scope.fn());
      }

      elem[0].addEventListener('click', callFn);
    }
  };
}]);

这是一个现场演示:http : //plnkr.co/edit/4QOGLD?p=info


2

这对我有用。

HTML使用指令

 <tr orderitemdirective remove="vm.removeOrderItem(orderItem)" order-item="orderitem"></tr>

指令的HTML:orderitem.directive.html

<md-button type="submit" ng-click="remove({orderItem:orderItem})">
       (...)
</md-button>

指令范围:

scope: {
    orderItem: '=',
    remove: "&",

0

我的解决方案:

  1. 在聚合物上引发事件(例如complete
  2. 定义将事件链接到控制功能的指令

指示

/*global define */
define(['angular', './my-module'], function(angular, directives) {
    'use strict';
    directives.directive('polimerBinding', ['$compile', function($compile) {

            return {
                 restrict: 'A',
                scope: { 
                    method:'&polimerBinding'
                },
                link : function(scope, element, attrs) {
                    var el = element[0];
                    var expressionHandler = scope.method();
                    var siemEvent = attrs['polimerEvent'];
                    if (!siemEvent) {
                        siemEvent = 'complete';
                    }
                    el.addEventListener(siemEvent, function (e, options) {
                        expressionHandler(e.detail);
                    })
                }
            };
        }]);
});

聚合物成分

<dom-module id="search">

<template>
<h3>Search</h3>
<div class="input-group">

    <textarea placeholder="search by expression (eg. temperature>100)"
        rows="10" cols="100" value="{{text::input}}"></textarea>
    <p>
        <button id="button" class="btn input-group__addon">Search</button>
    </p>
</div>
</template>

 <script>
  Polymer({
    is: 'search',
            properties: {
      text: {
        type: String,
        notify: true
      },

    },
    regularSearch: function(e) {
      console.log(this.range);
      this.fire('complete', {'text': this.text});
    },
    listeners: {
        'button.click': 'regularSearch',
    }
  });
</script>

</dom-module>

 <search id="search" polimer-binding="searchData"
 siem-event="complete" range="{{range}}"></siem-search>

searchData 是控制功能

$scope.searchData = function(searchObject) {
                    alert('searchData '+ searchObject.text + ' ' + searchObject.range);

}

-2

这应该工作。

<div my-method='theMethodToBeCalled'></div>

app.directive("myMethod",function($parse) {
  restrict:'A',
  scope: {theMethodToBeCalled: "="}
  link:function(scope,element,attrs) {
     $(element).on('theEvent',function( e, rowid ) {
        id = // some function called to determine id based on rowid
        scope.theMethodToBeCalled(id);
     }
  }
}

app.controller("myController",function($scope) {
   $scope.theMethodToBeCalled = function(id) { alert(id); };
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.