TL;DR How can I setup, in a controller, a $watch
to a DI'ed service's function return value?
My app has a service in order to make it easy to share data between controllers. I'm trying to setup a $watch
to one of those variables from a controller. However, I wanted to access through the service's getter for that particular variable.
This is the code in the controller
//this is the controller
var vm = this;
vm.isValidPhase = false;
activate();
////////////////
function activate() {
vm.isValidPhase = userProgressService.getCurrentPhaseValidity();
$scope.$watch('userProgressService.getCurrentPhaseValidity()',
function(newValue) {
vm.isValidPhase = newValue;
});
}
And in the userProgressService
I have:
//this is the service
var current = {
phaseNumber : 0,
phaseValidity : false,
phase : {},
userInput : {}
};
// ... ... ...
var exports = {
getCurrentPhaseData : getCurrentPhaseData,
getCurrentPhaseValidity : getCurrentPhaseValidity,
setCurrentPhaseValidity : setCurrentPhaseValidity,
getExistingUserInput : getExistingUserInput
};
return exports;
// ... ... ...
function getCurrentPhaseValidity() {
return current.phaseValidity;
}
// ... ... ...
In my unit tests to the controller using mocha, bard, chai and sinon, I'm doing:
describe('initial state', function () {
it('should be an invalid phase', function () {
expect(userProgressService.getCurrentPhaseValidity(),
'in service function').to.be.false;
expect(controller.isValidPhase,
'in controller scope').to.be.false;
});
});
And I'm getting an error on the second assertion:
AssertionError: in controller scope: expected undefined to be false
Now, from playing with commenting and uncommenting, I've noticed that the problem comes from the $watch
expression in the controller. But I don't know what is the problem with it... What am I doing wrong?
As you can see in the comments @JBNizet pointed out that the $watch
expression is wrong, instead, I should be watching the function itself, since the service is not defined in the $scope
. However, he also made me realize that, at least for this case, I didn't need to setup that $watch
. In his own words
[...] expose the function of the service in the scope (i.e.
$scope.getCurrentPhaseValidity = userProgressService.getCurrentPhaseValidity;
), and use<button ng-show="getCurrentPhaseValidity()">
I did as he told and changed the controller scope to
//... controller ...
vm.isValidPhase = userProgressService.getCurrentPhaseValidity;
Now I can use ng-show
and alike with isValidPhase
in the HTML under the controller.
As expected, it worked and fitted the purpose for which I wanted to watch the variable, which was to activate/deactivate a button based on its value.
For what I can think of, this solution covers much of whatever related problems you'd have with this.