1

I have this in my controller

$scope.activities = Activities.getAll();

$httpService.getActivities().then(function (response) {
    var activities = response.data.body;
    Activities.addMany(activities);
});

Now my service looks something like this:

var _activities = [];

function addMany(activities) {
    if (activities instanceof Array) {
        _activities = _activities.concat(activities);
    }
}

and:

function getAll() {
    return _activities;
}

The view does not update after getActivities resolves. I checked and the response does in fact contain new activities and they are added to the _activities array in the service.

What could be the problem and how can I make sure that the scope always changes based on the service values?

Thanks in advance.

2
  • May I ask why you are calling $scope.$apply() ? Commented Nov 9, 2016 at 1:34
  • I was trying to see if this actually helps. But I just removed it. @TimBiegeleisen Commented Nov 9, 2016 at 1:34

3 Answers 3

1

According to the docs (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) Javascript Array.concat method creates a new array.

This means that the _activities array returned from getAll function and resulting _activities array after addMany call are not the same array.

What you need to do is add elements to existing array not creating a new one. Instead of this:

_activities = _activities.concat(activities);

You should do it like this

for (var i=0; i < activities.length; i++) {
    _activities.push(activities[i]);
}
Sign up to request clarification or add additional context in comments.

3 Comments

While this technically works, I have explained below why this is not the best choice.
@MattWay The problem in this question is loss of reference and I have explained why it happens and how fix it. In your answer you overcome reference problem using a wrapper object but it is not necessary. It may be useful to do it your way in some cases but it will be overkill to wrap every reference. I think design pattern is outside of scope of this question so I have given the simplest answer.
I think you missed the point of my answer. You do not wrap every reference, you have a single wrapper for all references inside your service. One wrapping object, means you only need a single get() function, and also means that you don't need to worry about replacing references. With your method above you would need different get() functions for every reference, and also have to make sure that no reference is ever replaced, which would increase the likelihood of bugs, and make them more difficult to find. Good design patterns are always in the scope of a question.
1

It looks like your problem is how you reference your _activities variable from your service. When you call:

$scope.activities = Activities.getAll();

You are setting the initially blank array to be attached to the scope. After your addMany() function is called, the array inside the service is replaced, not updated. So your controller is still pointing to the original empty array.

In order to fix this problem, wrap your object in a container that you can set on scope. For example:

var data = {
    activities: []
};

function addMany(activities) {
    if (activities instanceof Array) {
        data._activities = _activities.concat(activities);
    }
}

function getData() { return data; }

Then in your controller do this:

$scope.activitySrv = Activities.getData();

To reference the activities in your view, you would then need to use activitySrv.activities.


The reason why this method should be preferred over simply pushing to the array (instead of replacing), is because this allows you to make your service extendable over time. You only need a single function in your service for retrieving data for scope usage, and can simply add to your parent object whenever you need to. It also groups all that services particular data onto a single scope variable, so it is easier to see in your view where the data came from.

Comments

0

Your view should be coupled to the scoped variables of the relevant controller. So presumably you should be using $scope.activities in your view. When new data comes in you should he updating this scoped variable. Try something like this in your controller:

$httpService.getActivities().then(function (response) {
    $scope.activities = response.data.body;
    Activities.addMany($scope.activities);
});

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.