1

I have a page where I display all the Persons from the DB, let's call it All Persons Page. On that page I have 2 options:

  • Add New Person
  • Delete Person (Whatever Person I want)

The problem appears when I am navigating to another page and then get back to the All Persons Page.

If I Delete a Person and after that Add a New Person, the view will display the New Person added but also the Deleted Person.

In the controller I am adding the data from the DB in an array.

$scope.persons_array = []

And display in the view via ng-repeat.

<div ng-repeat="(key, value) in persons_array)">

To Add a Person I am inserting a new ID in the array:

$scope.persons_array.push($scope.id[i])

To Delete a Person I am removing the spcified ID from the array:

$scope.persons_array.splice(i,1);

Tests:

To discover what the problem is I've created an setInterval function:

    setInterval(function(){
        console.log($scope.persons_array)
    }, 5000)

When I was on the All Persons Page, where I already had a Person added, I could see:

[Object { id=667,  $$hashKey="object:71"}]

When I navigate to another page and then get back:

[Object { id=667,  $$hashKey="object:71"}] //original
[Object { id=667,  $$hashKey="object:220"}] //new

Add a New Person:

[Object { id=667,  $$hashKey="object:71"}] //original
[Object { id=667,  $$hashKey="object:220"}, Object { id=668,  $$hashKey="object:227"}] //new

Detele the Person just added:

[Object { id=667,  $$hashKey="object:71"}] //original
[Object { id=667,  $$hashKey="object:220"}, Object { id=668,  $$hashKey="object:227"}] //new

Delete the Person that was already in the DB:

[] //original
[Object { id=667,  $$hashKey="object:220"}, Object { id=668,  $$hashKey="object:227"}] //new

Add Again a New Person:

[] //original
[Object { id=667,  $$hashKey="object:220"}, 
Object { id=668,  $$hashKey="object:227"}, 
Object { id=669,  $$hashKey="object:235"}] //new

From what I can see, In the moment I am navigating to another page, Angular is duplicating in some way the $scope.persons_array and after that is no longer updating the original array.

ThousandSteps
  • 67
  • 1
  • 7
  • By "navigating to another page" do you mean a different route within the angular app? Or literally another, separate page? (Angular is a SPA framework, remember, there's only one "page" in an angular app). – Daniel Beck May 18 '17 at 14:55
  • Also: in all those samples where you get back two consecutive arrays -- is there a five second delay between them? (Meaning you're showing the results of your setInterval running twice, first with stale data then with updated data?) Or are you saying you get two arrays returned from a single `console.log($scope.persons_array)`? (which, if so, would be.... surprising.) – Daniel Beck May 18 '17 at 14:58
  • (Last one, I promise:) Some of your code refers to persons_array, other code uses panels_array. Is that a typo in the question, or is it your actual code? – Daniel Beck May 18 '17 at 14:59
  • 1. Different route. 2. The arrays are returned from a single console.log() 3. It was a typo. – ThousandSteps May 18 '17 at 15:03
  • OK, thanks. My theory is that you've managed to get your controller persisting after the route closes, so that every time you revisit the route you're instantiating a new instance of the controller but the old one is still there churning out stale data. (If you navigate away and then back to the route a third time, do you get three arrays? That'd confirm my guess.) – Daniel Beck May 18 '17 at 15:13
  • Yes, you are right. If I do this for 3 times, I will get 3 arrays. – ThousandSteps May 18 '17 at 15:15
  • Good good, you just need to watch for $destroy in the controller on route change and release any resources / kill off your setInterval. See here: http://stackoverflow.com/questions/16094940/what-is-the-lifecycle-of-an-angularjs-controller#16096598 – Daniel Beck May 18 '17 at 15:17
  • You say that I should manually destroy the last scope? – ThousandSteps May 18 '17 at 15:24
  • Other way around; on $destroy, manually remove the persisting setInterval. See below. – Daniel Beck May 18 '17 at 15:33

1 Answers1

1

Controllers are not automatically released on route changes, so the setInterval you created in it keeps happily churning away forever using whatever data that particular instance of the controller had on hand at the time, even after you leave the route that was using it.

Angular sends a $destroy event when a controller is no longer attached to an active element; watch for that event in the controller and unbind any ongoing functions (such as your setInterval).

$scope.intervalID = setInterval(function(){
    console.log($scope.persons_array)
}, 5000);

$scope.on('$destroy', function() {
    clearInterval($scope.intervalID); 
    // and remove any other data that you don't need persisting
});

This can get pretty cumbersome if you store a lot of stuff on controller scope. Personally I tend to use controllers only as a container for idempotent functions, and instead store data on the directive (if it's instance-specific) or in a service (if it needs to be shared across the app) -- this solves the above problem, and also helps avoid scope leakage between directives that are using the same controller.

Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
  • The setInterval function was just to see what's happening. Your point is that in the controller is another function that keeps alive the controller when I navigate to another route? – ThousandSteps May 18 '17 at 15:38
  • AFAIK Angular doesn't automatically destroy controllers, it just fires the $destroy event. You're responsible for cleaning up as needed when that event fires. – Daniel Beck May 18 '17 at 15:44
  • I've discovered the problem. In the controller, my delete function was like this: `$(document).on('click', '.deletePerson', function(e){})`. I changed it into: `$scope.deletePerson = function(){}` and now is working fine. Thanks for your answer, it helped me find my problem and you also give me improving tips. – ThousandSteps May 18 '17 at 16:17
  • ah! I hadn't thought of that possibility. You could remove those DOM event hooks in the controller $destroy so they don't linger, but you're right it's better not to put it in the DOM in the first place. Glad I was able to help, even if my answer was sort of wrong :) – Daniel Beck May 19 '17 at 13:40