I have created a plunker to test binding $scope
on services. And I found that I can only bind to primitive values but reference values of a service.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">
<div ng-controller="BindToServiceCtrl as ctrl">
// I wonder why I could only bind on reference but primitive values of a service.
counterFactoryObj: {{counterFactoryObj}}<br/>
counterFactoryVal: {{counterFactoryVal}}<br/>
counterFactoryFun(): {{counterFactoryFun()}}<br/>
</div>
<script type="text/javascript">
var app = angular.module("BindToService", []);
app.controller("BindToServiceCtrl", function ($scope, CounterFactory) {
$scope.counterFactoryObj = CounterFactory.obj;
$scope.counterFactoryVal = CounterFactory.val;
$scope.counterFactoryFun = CounterFactory.fun;
});
app.factory("CounterFactory", function ($interval) {
var obj = [0];
var val = 0;
var fun = function () { return val; };
var addCounter = function () {
obj[0] += 1;
val += 1;
};
$interval(addCounter, 1000);
return {
obj: obj,
val: val,
fun: fun
};
});
</script>
</body>
In this demo, only counterFactoryObj
and counterFactoryFun()
change by time. I wonder how AngularJS binds values.
There are a few things here that I think are causing confusion.
First, when you return an object with:
return {
val: someVal
}
the property val
of the object is set at the time you return it to whatever the value was of someVal
. So, when you return your CounterFactory
service, CounterFactory.val === 0
- always - and it's not related to Angular's binding.
Second, with CounterService
service, your CounterService
instance has this.val
and in fact it is being updated with $interval (try it with console.log
). The problem there is that you assign its value in a particular point in time (e.g. when controller function runs) to the $scope.counterServiceVal
variable. Here, although CounterService.val
changes, the following remains true: $scope.counterServiceVal === 0
.
Btw, both .service
and .factory
return a singleton service instance as far as Angular is concerned, except in one your return a new-able Function and in the other - the actual instance, but that is just related to what Angular does to obtain the actual service instance - after the service is injected it behaves the same way.
And, finally, if you want to bind to a value - you can, but you need to actually change that value and bind to that changing variable. Here's how you'd do this with your CounterFactory
, as an example:
app.factory("CounterService", function ($interval) {
var svc = { val: 0 };
function addCounter() {
svc.val += 1;
};
$interval(addCounter, 1000);
return svc;
});
// in controller
$scope.counterService = CounterService;
<!-- in the view -->
<span>{{counterService.val}}</span>