baoqger baoqger -4 years ago 86
AngularJS Question

angularjs: can' t pass parameter to the parent controller method via '&' binding in isolated directive scope

In my angular application, I defined a directive

foo-directive
, which is placed in a parent controller as below:

<div ng-app="app">
<div ng-controller="ParentCtrl as parent">
<foo-directive data="parent.city" tab-click="parent.tClick()" tab-click2="parent.tClick2(v)"></foo-directive>
</div>
</div>


pass two method:
parent.tClick()
and
parent.tClick2(v)
into the directive and bind to
tab-click
and
tab-click2
attributes respectively. The difference is that the second one has a parameter


and the JS code goes as below:

function ParentCtrl($timeout){
this.city= "London";
this.tClick = function(){
console.log("debugging parent tclick...");
}
this.tClick2 = function(v){
console.log("debugging parent tclick2...");
console.log(v)
}
}

function FirstCtrl() {
this.$onInit = function(){
this.click = function(){
this.tabClick();
this.tabClick2("abc");
}
}
}

function fooDirective() {
return {
scope: {
data: '=',
tabClick : "&",
tabClick2: "&"
},
controller: 'FirstCtrl',
controllerAs: 'foo',
bindToController: true,
template: '<div ng-click="foo.click()">{{ foo.data }}</div>',
link: function ($scope, $element, $attrs, $ctrl) {
//console.log($ctrl.name);
}
};
}


Now the issue comes from the second method
this.tabClick2("abc")
. There is
TypeError
message. I have reproduced this issue with this live demo:

https://jsfiddle.net/baoqger/sv4d03hk/1/

any help?

Answer Source

When passing the functions into your directive, you should pass the "reference" to the function, rather than the "result" of the function. Adding the parenthesis, is actually executing the function and returning the result to the directive. As neither function returns a value, they will both be passing undefined.

Given that the value of the parameter you want to pass (v) to the function is inside the directive scope, not that of the parent, you dont need to even tell the directive that the function accepts a parameter. You just pass it to the function inside the directive. ie.

<foo-directive data="parent.city" tab-click="parent.tClick" tab-click2="parent.tClick2"></foo-directive>

According to the docs, using & is then you want to evaluate the result of the attribute:

The & binding allows a directive to trigger evaluation of an expression in the context of the original scope, at a specific time.

Instead, we want to be able to execute the passed attribute, and in particular give it a variable. As such either = (two-way binding) or @ (one-way binding) are probably more appropriate for what we're after.

tabClick: "=",
tabClick2: "="

You can also do away with your controller completely by updating your template.

template: '<div ng-click="foo.tabClick();foo.tabClick2(data)">{{ foo.data }}</div>'

Updated JSFiddle

function ParentCtrl($timeout) {
  this.city = "London";
  this.tClick = function() {
    console.log("debugging parent tclick...");
  }
}

function FirstCtrl() {}

function fooDirective() {
  return {
    scope: {
      data: '=',
      tabClick: "="
    },
    controller: 'FirstCtrl',
    controllerAs: 'foo',
    bindToController: true,
    template: '<div ng-click="foo.tabClick(data)">{{ foo.data }}</div>',
    link: function($scope, $element, $attrs, $ctrl) {
      //console.log($ctrl.name);
    }
  };
}

angular
  .module('app', [])
  .directive('fooDirective', fooDirective)
  .controller('FirstCtrl', FirstCtrl)
  .controller('ParentCtrl', ParentCtrl)
<script src="https://code.angularjs.org/1.6.2/angular.min.js"></script>
<div ng-app="app">
  <div ng-controller="ParentCtrl as parent">
    <foo-directive data="parent.city" tab-click=":: parent.tClick"></foo-directive>
  </div>
</div>

PS. if you're concerned about performance using @ or =, consider one time bindings using ::. ie. <foo-directive data="parent.city" tab-click=":: parent.tClick" tab-click2=":: parent.tClick2"></foo-directive>

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download