Reputation: 1
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
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