Define function inside Angular directive's isolated scope

14,522

Solution 1

The only legal values for an isolated scope object definition are strings which begin with &, @, or =. (You can also follow these symbols with the name of the property on the parent scope should you want the property name to differ within the directive.1) In this instance you want to use the = symbol as this indicates that Angular should "bind changePage on the directive scope to changePage on the parent scope". Take a look at this answer for more details.

You're right that it is ugly having to pass this function in via an attribute on the directive, however this is the nature of having an isolated scope. You might consider having the changePage and other related methods in a service, as you can then inject this as a dependency.

Edit

Without an isolate scope you could do the following:

module.directive("pagination", function() {    
    return {
        restrict: "E",
        templateUrl: "templates/pagination.html",
        link: function(scope, elem, attrs) {
            // scope.changePage is available from the directive's parent $scope
        }
    }
}

1 Example: { 'nameInDirective': '=nameOnParent' }

Solution 2

As user sdgluck answers correctly you can extend the directive scope in the link function and thus keep your code clean and separate. Following sample is a little more complete but it boils down to the same result. It allows you to define an element as below. Be aware that all attributes are interpreted as objects, and, if you want to pass a string value you have to put the extra quotes around that value (or you'll get an undefined value for that attribute)

<my-button someattr="'my text'"></my-button>

angular.module('test', []);

angular.module('test').controller('testctrl', function($scope) {
  $scope.someValue = '';
});

angular.module('test').directive('myButton', function() {
  return {
    restrict: 'E',
    template: '<button>{{getText()}}</button>',
    replace: true,
    scope: {
      someattr: '='
    },
    link: function(scope, elements, attrs){
      scope.getText = function(){
        return scope.someattr;
      };
    }
  }
});
Share:
14,522

Related videos on Youtube

Robert Kusznier
Author by

Robert Kusznier

Programmer, web developer, vegan. Born in 1989 in Poland. My profiles: GitHub | LeetCode What I like: Creating things, which are useful and solve real problems that our world faces, Learning and trying new things, both in programming and other fields, Clean code, good architecture, good design, JavaScript, Python, Haskell, Vim, People and other creatures, Cooking, David Lynch, Béla Tarr, Tom Waits and other cinema and music, Honesty. What I don't like: Writing and working on crappy code, Things that don't work as they should, Laziness, Wasting things.

Updated on June 22, 2022

Comments

  • Robert Kusznier
    Robert Kusznier about 2 years

    I'm writing a custom <pagination> directive, which will return elements responsible for changing the current page and pagination settings (e.g. how many items will be displayed per page). The directive has isolated scope (to make it more reusable).

    Inside the directive template I need to call functions like changePage() or changePaginationSettings(). The only way to pass a function inside the isolated scope I've found so far is define the function in the controller.

    mainController.js

    module.controller("mainController", function($scope) {
        $scope.changePage = function() { ... };
    });
    

    and then pass it to directive as an attribute:

    pagination.js

    module.directive("pagination", function() {
        return {
            restrict: "E",
            scope: {
               changePage: "="
            },
            templateUrl: "templates/pagination.html"
        }
    }
    

    pagination.html

    <pagination change-page="changePage">
    

    This looks very ugly to me, because it's splitting related code into 2 unrelated files. That is changePage() function should be defined in the pagination.js file, not in mainController.js.

    I'd think something like that should be possible:

    pagination.js

    module.directive("pagination", function() {
        function changePage() { ... };
    
        return {
            restrict: "E",
            scope: {
               changePage: changePage
            },
            templateUrl: "templates/pagination.html"
        }
    }
    

    But such code is producing a (meaningless to me) error: Error: definition.match is not a function.

    Is there a way to achieve that? I mean to pass a function defined in the same file inside an isolated scope of directive.

    I've read AngularJS Directive Docs but they don't even list what are legal values in scope object of a directive, only give some "=", "&" and "@" examples.

  • Robert Kusznier
    Robert Kusznier almost 9 years
    So there is no way to define a new scope value inside a directive itself, right? BTW I don't see how allowing that would violate an isolated scope concept. Solution using service seems to be much nicer than defining the values inside a controller. Thanks for suggesting it.
  • Robert Kusznier
    Robert Kusznier almost 9 years
    That is almost the same as the code I provided. And what's the most important you still define changePage() function inside a controller, which is something that I'd like to avoid and the reason of my question - I've asked is there a way to define changePage() inside a directive rather than in a controller.
  • sdgluck
    sdgluck almost 9 years
    You can do whatever you like with the directive's $scope, the only difference with an isolate scope is that the directive inherits properties defined explicitly by you and none others. Access a directive's $scope just like you would in a controller, e.g.: link: function(scope, elem, attrs) { ... }
  • Robert Kusznier
    Robert Kusznier almost 9 years
    I've asked the question in my previous comment in the wrong way. What I meant was: "So there is no way to define some new value inside a directive? - the only values available inside a directive are the ones that were passed to it via attributes and bound to the isolated scope explicitly by me."
  • sdgluck
    sdgluck almost 9 years
    Not sure if you are still posing the question? But yes, that is correct.
  • Robert Kusznier
    Robert Kusznier almost 9 years
    Yes, that was a question : ). I wanted to make sure my thinking is correct. Thanks for clarification and answering my question.
  • SFernando
    SFernando almost 9 years
    ok..anyway you would have missed the isolate scope when you need it, as a result of that you can not pass params from parent scope to your directive
  • Robert Kusznier
    Robert Kusznier almost 9 years
    I didn't want to pass anything from the parent scope. What I'd want is to define a new value (in my case a function) inside the isolated scope of a directive.