Reputation: 2324
I have a service that asynchronously fetches data and provides this to a controller. There, it is attached to the $scope
and used in the view.
What I do not understand is the differences in updating, depending on how this service is used in the controller.
The service is not providing promises to data, but rather exposing the data itself. I've simulated it below with timeouts:
(edit: I've added initialisation to the Opt
variable to show that that does not solve the issue.)
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;
}]);
This is then used in the controller like so:
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've created a live demo in Plnkr here.
As you can see, the second select
element in the view (and the 'symbol' property of $scope
) is updated after 4 instead of 2 seconds. If there were no $timeout
in the controller, this would not happen at all of course.
Why is this so? Why are the changes not cascaded through?
Thanks!
Upvotes: 3
Views: 59
Reputation: 1503
You are trying to assign $scope.symbols
to $scope.options.symbols
or Options.symbols
before the symbols
array exists in the object returned by the Options
service, so it ($scope.symbols
) is assigned undefined
rather than a reference to the symbols object. You can see this by adding a couple console statements before you attempt the assignments. Since the symbols
object doesn't yet exist, your $scope.symbols
variable can't reference it.
This isn't a problem with people
or cities
because you're accessing them through the $scope.options
object which references the Opt
object returned by the Options
service. Since the base reference remains constant, your ng-repeats
see changes to the people
and cities
children objects.
If you really want to assign Options.symbols
to $scope.symbols
, you could accomplish it with a watch on $scope.options.symbols
:
$scope.$watch('options.symbols', function(value) {
$scope.symbols = value;
});
Edit after the example plunker was updated
The revised plunker still exhibits the same behavior because the initialized people
, cities
, and symbols
are being overwritten in the $timeout
. If you want to keep the reference the same you must not use the =
operator in the $timeout
. One option would be to iterate through the returned list and add each item with a push:
$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 accomplish this, but the key point is you can not reassign Opt.symbols
to some other object (even if that other object is also an array).
Another approach would be to change Opt.symbols
to be an object with an array inside it, your reference $scope.symbols
points to the object (Opt.symbols
) and can look at the 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 that route, you would need to adjust 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 you could take beyond the two I've given examples for, which makes sense for your project will have to be a decision for you to make. The key point is that in the $timeout
you cannot reassign the value of Opt.symbols
and expect $scope.symbols
to reference the new value.
Upvotes: 1
Reputation: 7976
In index.html you correctly specified the ng-options for 'options.people' and 'options.options' but forgot to do so for 'symbols'
Before:
ng-options="s as s.name for s in symbols"
After:
ng-options="s as s.name for s in options.symbols"
Here's the new plunker:
http://plnkr.co/edit/HqPQM5Qqfq7gi9KWQLKJ?p=preview
Upvotes: 0