Dirty checking with shared service between controllers, One way works the other does not?

While attempting to answer a question regarding sharing data between two separate controllers I ran into a question .

I usually use services for for this task and began to create a jsfiddle, but I could not get it to work.

After a bit of debugging if I created the properties dynamically in setActivePersonWorks(person) the dirty checking worked and the second controller showed the correct value.

If I assigned the value in setActivePersonDoesNotWork() it did not.

If I used $timeout() I was able to verify that DataService.badPerson did indeed contain the correct data.

Am I doing something wrong? I guess if you do something with $apply() it will work correctly, but why does creating the values dynamically cause things to just work?

Working Example:

var myTest = angular.module("MyTest", []);
myTest.factory("DataService", function () {
    var People = {
        goodPerson: {},
        badPerson: {},
        setActivePersonWorks: function (person) {
            People.goodPerson.name = person.name;
            People.goodPerson.id = person.id;
        },
        setActivePersonDoesNotWork: function (person) {
            People.badPerson = person;
        }
    };
    return People;
});

function ViewController($scope, DataService, $timeout) {
    $timeout(function () {
        DataService.setActivePersonWorks({
            id: 1,
            name: "Good Mark"
        });
        DataService.setActivePersonDoesNotWork({
            id: 2,
            name: "Bad Mark"
        });
    }, 1000);
}

function DetailController($scope, DataService, $timeout) {
    $scope.goodPerson = DataService.goodPerson;
    $scope.badPerson = DataService.badPerson;

    $timeout(function(){
        $scope.message = "DataService has the value: " + DataService.badPerson.name + " but $scope.badPerson is " + $scope.badPerson.name;
    }, 2000);
}

The <html/>

<div ng-app="MyTest">
    <div ng-controller="ViewController"></div>
    <div ng-controller="DetailController">
         <h1>Works: {{goodPerson.name}}</h1>

         <h1>Does Not Work: {{badPerson.name}}</h1>
        {{message}}
    </div>
</div>

On jsfiddle

Answers:

Answer

When Angular sees

<h1>Does Not Work: {{badPerson.name}}</h1>

it sets up a $watch on object badPerson. Looking at your controller, $scope.badPerson is a reference to object DataService.badPerson. All is fine so far... the problem happens here:

setActivePersonDoesNotWork: function (person) {
    People.badPerson = person;
}

When this function executes, badPerson is assigned a new/different object reference, but the controller is still $watching the old/original object reference.

The fix is to use angular.copy() to update the existing badPerson object, rather than assigning a new reference:

setActivePersonDoesNotWork: function (person) {
    angular.copy(person, People.badPerson);
}

This also explains why setActivePersonWorks() works -- it does not assign a new object reference.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.