$ selective update by service resolution
Problem
I have a service that fetches data asynchronously and exposes that to a controller. There it is bound to $scope
and used in the view.
What I don't understand is the update differences, depending on how this service is used in the controller.
Example
The service does not provide promises for the data, but rather represents the data itself. I modeled it below with timeouts:
(edit: I've added initialization to the variable Opt
to show that this doesn't solve the problem.)
app.service("Options", ["$q", "$timeout", function ($q, $timeout) {
var Opt = {
people:[],
symbols:[],
countries:[]
};
$timeout(function () {
Opt.people = [ {frst: "Johnny", last: "Walker"},
{frst: "Jack", last: "Daniels"},
{frst: "Jim", last: "Bean"} ];
}, 1000);
$timeout(function () {
Opt.symbols = [ {name: "Pi", descr: "circles"},
{name: "Phi", descr: "ratios"},
{name: "Psi", descr: "waves"},
{name: "Chi", descr: "distributions"} ];
}, 2000);
$timeout(function () {
Opt.cities = [ {name: "Amsterdam", country: "Netherlands"},
{name: "Cairo", country: "Egypt"},
{name: "Santiago", country: "Chile"} ];
}, 3000);
/* etc */
return Opt;
}]);
Then it is used in the controller like this:
app.controller("Ctrller", ["$scope", "Options", "$timeout", function ($scope, Options, $timeout) {
$scope.options = Options; //updating
$scope.symbols = $scope.options.symbols; //not updating
$scope.symbols = Options.symbols; //not updating either
$timeout(function(){
$scope.symbols = $scope.options.symbols; //updating
}, 4000);
}]);
I created a demo in Plnkr here .
As you can see, the second item select
in the view (and the 'symbol' property for $scope
) is updated after 4 instead of 2 seconds. If it wasn't in the controller $timeout
, it certainly won't happen.
Why is this so? Why aren't the changes cascading through?
Thank!
source to share
You are trying to assign $scope.symbols
$scope.options.symbols
or Options.symbols
before the array symbols
exists in the object returned by the service Options
, so it is $scope.symbols
assigned ( ) undefined
rather than a symbol object reference. You can see this by adding a few console statements before attempting assignments. Since the object symbols
doesn't exist yet, your variable $scope.symbols
cannot refer to it.
This is not a problem with people
or cities
because you are accessing them through an object $scope.options
that refers to the object Opt
returned by the service Options
. Since the base link remains constant, yours ng-repeats
sees changes in the child objects people
and cities
.
If you really want to assign Options.symbols
to $scope.symbols
, you can execute it with a clock on $scope.options.symbols
:
$scope.$watch('options.symbols', function(value) {
$scope.symbols = value;
});
Change after plunger update
Revised plunker continues to display the same behavior because initialized people
, cities
and symbols
overwritten $timeout
. If you want to keep the link the same, you shouldn't use the operator =
in $timeout
. One option would be to iterate over the returned list and add each item with a click:
$timeout(function () {
results = [ {name: "Pi", descr: "circles"},
{name: "Phi", descr: "ratios"},
{name: "Psi", descr: "waves"},
{name: "Chi", descr: "distributions"} ];
angular.forEach(results, function(result) {
Opt.symbols.push(result);
});
}, 2000);
Here is a revised plunker with my suggested changes.
There may be better ways to do this, but the key point is that you cannot reassign Opt.symbols
to any other object (even if that other object is also an array).
Another approach would be to change Opt.symbols
how an object with an array inside it, your reference $scope.symbols
points to an object ( Opt.symbols
) and can look at internal values.
Opt = {
people: [],
symbols: {},
cities: []
}
$timeout(function() {
Opt.symbols.values = [ {name: "Pi", descr: "circles"},
{name: "Phi", descr: "ratios"},
{name: "Psi", descr: "waves"},
{name: "Chi", descr: "distributions"} ];
}, 2000);
If you went this route you will need to tweak your ng options to look like,
ng-options="s as s.name for s in symbols.values"
Here is a working plunker of this approach.
There are other approaches that you could take outside of the two examples given that make sense for your project and you need to make a decision. The key point is that $timeout
you cannot reassign a value Opt.symbols
and expect to $scope.symbols
refer to a new value.
source to share