Robert Kusznier
Robert Kusznier

Reputation: 6911

Define function inside Angular directive's isolated scope

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.

Upvotes: 11

Views: 14653

Answers (4)

Lorena Pita
Lorena Pita

Reputation: 1516

I had the same issue and I defined my function inside a custom directive instead of defining the function inside a controller. Here you can see an example:

Your HTML:

<my-custom-directive></my-custom-directive>

Your directive definition:

.directive("myCustomDirective", myCustomDirectiveHanlder)

function myCustomDirectiveHandler() {
    return {
        templateUrl: "/app/templates/myCustomDirectiveTemplate.html",
        restrict: 'E',
        link: function (scope, elements, attrs) {
            //your code here
        }
    }
};

Upvotes: 0

sdgluck
sdgluck

Reputation: 27217

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

Upvotes: 13

Wim Van Houts
Wim Van Houts

Reputation: 594

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

Upvotes: 2

SFernando
SFernando

Reputation: 1124

here you have a sample configuration.

main controller

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

let's say it's index.html and you should have the directive like that

<pagination change-page="changePage()">

and your directive should be like below

module.directive("pagination", function() {
    function changePage() { ... };

    return {
        restrict: 'E',
        scope: {
           changePage: '&'
        },
        templateUrl: "templates/pagination.html"
    }
}

and your pagination.html

<div ng-click="changePage()"></div> 

this will bind to your main controller's changePage function.

Upvotes: -1

Related Questions