Angular - Bind to function that returns a promise
angularjs, javascript
Solution
You're resolving your promise, but not with a value--so the value of the promise on the `$scope` when resolved is `undefined`, which is falsy, thus your `ng-show` is not triggering.
It seems you're looking for something more like this:
In the service:
function handleLoggedInStatus(status) {
if (status) {
self.authenticated = true;
}
deferred.resolve(status); // always resolve, even if with falsy value
}
if (this.authenticated === null) {
$http.get('/user')
.success(function(response) {
handleLoggedInStatus(response.success);
})
.error(function(data) {
deferred.reject(data.errorMsg); // reject if there was an error
});
} else {
handleLoggedInStatus(this.authenticated);
}
In the controller:
$scope.loggedIn = sessionService.isLoggedIn();
In the HTML:
<div ng-show='loggedIn'>...</div>
Here is a JSFiddle demonstrating resolving the deferred with a truthy value and binding to the `$scope`.
Note that you can't bind the function itself to the scope
$scope.loggedIn = sessionService.isLoggedIn
and call the function in the view
<div ng-show="loggedIn()">...</div>
because the function returns a different promise each digest cycle (which is why you were getting the '10 digest cycles' error). You could, however, ensure that extra calls to `sessionService.isLoggedIn` returns the same promise instead of creating a new one, since you can call `then` on a promise multiple times (and in fact this is one of the benefits of promises):
deferred = null;
isLoggedIn: function() {
if (!deferred) {
deferred = $q.defer();
$http.get('/user')
.success(function(response) {
deferred.resolve(response.success); // resolve if true or false
})
.error(function(data) {
deferred.reject(data.errorMsg); // reject if there was an error
});
}
return deferred.promise;
}
You could then get rid of the `this.authenticated` boolean, as you do not need to keep track of a previously-logged-in user across function calls (since the promise does this for you).
However, while this gets rid of the digest cycle error, you still cannot call the function from the view--I suspect Angular is treating the return value (the promise itself) as a truthy value, rather than binding to the promise's resolved value. Here's an example of it not working; notice the `div` is displayed even though the promise is resolving with `false`.
To use `deferred.reject` to indicate the user was not authenticated, as in your original service, you'd want to do something more like this in the controller, though I believe that `resolve`ing with `false` is cleaner:
sessionService.isLoggedIn()
.then(function() {
$scope.loggedIn = true; // resolved
}).then(function() {
$scope.loggedIn = false; // rejected
});
Problem
I am new to angular, and I am having a tough time getting to the bottom of this problem. I am writing a single-page application, and am working on the authentication portion. I have a service called "sessionService" that I want to be able to use throughout the app to determine if the user is logged in or not. It is simple if I do something like this: ``` ...service('sessionService', function(...) { /*...snip...*/ this.isLoggedIn = function() { return this.authenticated; }; }); ``` Where "authenticated" is just private to the service. However, the falls apart if I refresh the page. So, my thought was to do something like this: ``` /*...snip...*/ this.isLoggedIn = function() { var deferred = $q.defer() , self = this ; function handleLoggedInStatus(status) { if (status) { self.authenticated = true; deferred.resolve(); } else { deferred.reject(); } } if (this.authenticated === null) { $http.get('/user') .success(function(response) { handleLoggedInStatus(response.success); }); } else { handleLoggedInStatus(this.authenticated); } return deferred.promise; }; ``` And then in my controller I would do something like this: ``` $scope.isLoggedIn = sessionService.isLoggedIn; ``` And in my template I would do: ``` ...data-ng-show="isLoggedIn()" ``` However, doing that would result in the following error: ``` 10 $digest() iterations reached. Aborting! ``` I tried a few different ways of referencing the sessionService.isLoggedIn function, such as: ``` $scope.isLoggedIn = sessionService.isLoggedIn(); $scope.isLoggedIn = sessionService.isLoggedIn.bind(sessionService)(); $scope.isLoggedIn = function() { return sessionService.isLoggedIn() } ``` But they either didn't work, or just gave me the same error. Basically, I just want to be able to return a promise that will tell me whether or not the user is logged in. If we don't know if they are logged in (like after a page refresh), the promise will be resolved after an ajax request. If we do know already (like with normal navigation throughout the single page app) then the promise will be resolved immediately. I would then like to use that in my views so I can show/hide certain things, such as links to logout or view the account page. What am I doing wrong?