I have a service that does something hard and returns a promise:
.factory('myService', function($q) {
return {
doSomethingHard: function() {
var deferred = $q.defer();
setTimeout(function() {
deferred.resolve("I'm done!");
}, 1000);
return deferred.promise;
}
};
})
I have a controller that adds a function to the scope using that service:
.controller('MyCtrl', function($scope, myService) {
$scope.doSomething = function() {
var promise = myService.doSomethingHard();
promise.then(function(result) {
alert(result);
});
};
})
I use a directive to call that controller function by parsing an attribute:
.directive('myDirective', function($parse) {
return {
link: function(scope, el, attr) {
var myParsedFunction = $parse(attr.myDirective);
el.bind('click', function() {
myParsedFunction(scope);
});
}
};
})
with the template
<div ng-controller="MyCtrl">
<button my-directive="doSomething()">The Button</button>
</div>
Clicking the button triggers the event listener, which calls the controller function doSomething
, which calls the service function doSomethingHard
, which returns a promise, THAT IS NEVER RESOLVED.
Whole thing up on a fiddle here:
http://jsfiddle.net/nicholasstephan/RgKaT/
What gives?
Thanks.
EDIT: Thanks to Maksym H., It looks like wrapping the promise resolve in $scope.$apply()
makes it fire in the controller. I've got a working fiddle up http://jsfiddle.net/RgKaT/4/. But I'd really like to keep the scope out of my services.
I'd also really like to know why this works. Or better yet, why it doesn't work without resolving the promise while wrapped in a scope apply. The whole Angular world vs regular Javascript world analogy makes sense when thinking about properties as changes need to be digested, but this is a promise... with callback functions. Does $q just flag the promise as resolved and wait for the scope to digest that property update and fire its resolved handler functions?
Just replace
setTimeout
with$timeout
(and remember to inject$timeout
into your service). Here's an updated jsFiddle.Here is another way: Try to define scope in directive and bind this attr to expect parent scope.
But the main thing is to run digest when you resolving it after some time:
jsFiddle
P.S. Make sure you are passing method as model of parent scope not applying this by "()" in HTML
<button my-directive="doSomething">Button</button>