Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use ng-click on a directive with isolate scope?

I can get ng-click to work when the scope is inherited on a directive but not when isolated. UPDATE: The point is that I want the click function to be defined as part of the directive... moving the function definition into a different scope is not what I want.

Here's the working example with inherited scope: https://codepen.io/anon/pen/PGBQvj

Here's the broken example with isolated scope; https://codepen.io/anon/pen/jrpkjp

(Click the numbers, they increment in the first example but not in the second)

Some code...

The HTML

<div ng-app="myApp" ng-controller="baseController">
  <my-directive ng-click="hello()" current="current"></my-directive>
</div>

The directive with inherited scope:

angular.module('myApp', [])
    .controller('baseController', function($scope) {
    $scope.current = 1;
  })
    .directive('myDirective', function() {
    return {
      link: function(scope, element, attrs) {      
        scope.hello = function() {
          scope.current++
        };
      },
      replace: true,
      scope: true,
      template: '<div><child>      <strong>{{ current }}</strong></child></div>'
    }
    })
  .directive('child', function() {
    return {
      link: function(scope, element, attrs) {
        console.log("horeee");
      }
    }
  });

The same directive but with isolated scope:

angular.module('myApp', [])
    .controller('baseController', function($scope) {
    $scope.current = 1;
  })
    .directive('myDirective', function() {
    return {
      link: function(scope, element, attrs) {      
        scope.hello = function() {
          scope.current++
        };
      },
      replace: true,
      scope: {
        current:'='
      },
      template: '<div><child>      <strong>{{ current }}</strong></child></div>'
    }
    })
  .directive('child', function() {
    return {
      link: function(scope, element, attrs) {
        console.log("horeee");
      }
    }
  });
like image 518
geoidesic Avatar asked Oct 29 '22 18:10

geoidesic


1 Answers

The problem is you're trying to call a function that is undefined. If you wish the logic to be defined inside the isolated directive, there is no need to pass in a function reference.

<my-directive current="current"></my-directive>

You cannot pass ng-click="hello()" here. This is the scope of the controller, so hello() is undefined.

Instead, move the ng-click event to the template of the directive

template: '<div ng-click="hello()">

One additional point: You're using the link() function of the directive. This is reserved for DOM manipulation. Instead, define hello() within the controller function.

controller: function ($scope) {
  $scope.hello = function() {
      $scope.current++
  }
},

I think there is a larger architectural problem here, though. The point of an isolated directive, or component, is to encapsulate logic internal to itself. It should not manipulate external state. In this example, you're incrementing a number. What if, in another part of your application, you wish to decrement a number? Copying the directive with decrement logic would be a lot of code duplication.

Instead, you should define the increment, or decrement, functionality in the controller, and pass it through to the directive.

<my-directive change-number="increment()" current="current"></my-directive>

Then, use the & syntax to bind the function reference to the directive:

scope: {
  current:'=',
  changeNumber: '&'
},

and call changeNumber from the template. Doing so very much facilitates code reuse.

like image 105
kyranjamie Avatar answered Nov 15 '22 09:11

kyranjamie