Danny
Danny

Reputation: 1

angularjs 1.7.2 - How to call a directive function from a controller?

How do you call a directive's function from within a controller?

I have seen a number of answers to this question, with the solution similar to the following:

https://lennilobel.wordpress.com/tag/calling-directive-from-controller/

I have implemented it as follows:

Directive

angular.module('myApp').directive('myDirective', ['$log',

function ($log) {

    function link(scope, element, attributes) {

        //function that a controller can call
        scope.myFunc = function () {
            //Do Something
        };


        //if the user has provided an accessor, attach the function
        if (scope.accessor) {
            scope.accessor.myFunc = scope.myFunc;
        }
    }

    return {
        link: link,
        restrict: 'E',
        templateUrl: 'app/myTemplate.html',
        scope: {
            accessor: '=',
        }
    }
}

Controller

angular.module('myApp').controller('myCtrl', ['$log', '$q',

function ($log, $q) {

    var vm = this;

    // empty object that the directive will attach myFunc to
    vm.accessor = {};

    $q
        .all([
            //Service Call
        ])
        .then(function (results) {
                //manipulate the results of the service call using the
                //directives function
                vm.callDirective();
            },
            function (err) {
                $log.debug('$q.all err:', err);
            });



    vm.callDirective = function () {
        if (vm.accessor.myFunc) {
            vm.accessor.myFunc();
        } else {
            $log.error('umm, I don\'t have a function to call');
        }
    };
}

HTML Template

<div ng-controller="myCtrl">
    <myDirective accessor="vm.accessor"></myDirective>
</div>

When I run the code, the directive indicates that accessor is undefined. As a result, accessor, in the controller, doesn't have myFunc defined.

How do I get myFunc to execute?

I am using angular 1.7.2

Upvotes: 0

Views: 430

Answers (1)

Oluwafemi Sule
Oluwafemi Sule

Reputation: 38982

The controller is compiled (an instance created with the resulting scope) before the directive.

In this scenario, it compiles faster than the directive can set the accessor function.

A quick workaround for this is to set a delay before checking if there is an accessor present using $timeout service.

The key is having a Promise object passed to $q.all. This will cause a small delay and allowing for the directive to be compiled.

For real, you'll be having promises that do some network call passed to $q.all instead of doing this workaround with the $timeout service.

Here is how this will go:

index.html

<div ng-controller="myCtrl as vm">
  <my-directive accessor="vm.accessor"></my-directive>
</div>

script.js

const myApp = angular.module('myApp', []);

myApp.directive('myDirective', ['$log', myDirective]);

myApp.controller('myCtrl', ['$scope', '$timeout', '$log', '$q', myCtrl]);

function myCtrl($scope, $timeout, $log, $q) {
  const vm = $scope.vm;

  // empty object that the directive will attach myFunc to
  vm.accessor = {};

  vm.callDirective = () => {
    if (vm.accessor.myFunc) {
      vm.accessor.myFunc();
    } else {
      $log.error("umm, I don't have a function to call");
    }
  };

  const handleSuccess = results => {
    //manipulate the results of the service call using the
    //directives function
    vm.callDirective();
  };

  const handleError = err => {
    $log.debug('$q.all err:', err);
  };

  $q.all([
    //Service Call
    $timeout()
  ])
    .then(handleSuccess)
    .catch(handleError);
}

function myDirective($log) {

  //function that a controller can call
  const myFunc = function() {
    //Do Something
    $log.info('Calling assessor myFunc');
  };

  const link = function(scope) {
    //if the user has provided an accessor, attach the function
    if (scope.accessor) {
      scope.accessor.myFunc = myFunc;
    }
  };

  return {
    link: link,
    restrict: 'E',
    templateUrl: 'mydirective.html',
    scope: {
      accessor: '='
    }
  };
}

Upvotes: 1

Related Questions