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

我想创建一个指令链接到一个属性。属性指定应该在作用域上调用的函数。但是我还想传递一个参数给在 link 函数内部确定的函数。

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

在 link 函数中,我绑定到一个 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 我就可以让它工作,但是一旦我尝试传递参数,函数就不再被调用了

153770 次浏览

Not knowing exactly what you want to do... but still here's a possible solution.

Create a scope with a '&'-property in the local scope. It "provides a way to execute an expression in the context of the parent scope" (see the directive documentation for details).

I also noticed that you used a shorthand linking function and shoved in object attributes in there. You can't do that. It is more clear (imho) to just return the directive-definition object. See my code below.

Here's a code sample and a fiddle.

<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>

Marko's solution works well.

To contrast with recommended Angular way (as shown by treeface's plunkr) is to use a callback expression which does not require defining the expressionHandler. In marko's example change:

In template

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

In directive link function

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

This does have one disadvantage compared to marko's solution - on first load theMethodToBeCalled function will be invoked with myParam === undefined.

A working exampe can be found at @treeface Plunker

Just to add some info to the other answers - using & is a good way if you need an isolated scope.

The main downside of marko's solution is that it forces you to create an isolated scope on an element, but you can only have one of those on an element (otherwise you'll run into an angular error: Multiple directives [directive1, directive2] asking for isolated scope)

This means you :

  • can't use it on an element hat has an isolated scope itself
  • can't use two directives with this solution on the same element

Since the original question uses a directive with restrict:'A' both situations might arise quite often in bigger applications, and using an isolated scope here is not a good practice and also unnecessary. In fact rekna had a good intuition in this case, and almost had it working, the only thing he was doing wrong was calling the $parsed function wrong (see what it returns here: https://docs.angularjs.org/api/ng/service/$parse ).

TL;DR; Fixed question code

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

and the code

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); };
}

This should work.

<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); };
}

You can create a directive that executes a function call with params by using the attrName: "&" to reference the expression in the outer scope.

We want to replace the ng-click directive with ng-click-x:

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

If we had this scope:

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


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

We could write our directive like so:

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);
}
};
}]);

Here is a live demo: http://plnkr.co/edit/4QOGLD?p=info

My solution:

  1. on polymer raise an event (eg. complete)
  2. define a directive linking the event to control function

Directive

/*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);
})
}
};
}]);
});

Polymer component

<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>

Page

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

searchData is the control function

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


}

Here's what worked for me.

Html using the directive

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

Html of the directive: orderitem.directive.html

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

Directive's scope:

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