質問

I've set up a service to share some data/state between multiple controllers. In one controller, I update some of the service properties with scope data through a save function. That data is then used in the other controllers by assigning a scope value to the service. The problem is, depending on the type of data that the first controller assigns to the service, their is different behavior. If the model data is a primitive value then the service's property will ONLY be updated when the save function is ran. However, if the model data is an object, it will continue to update the service as the model data is changed. I'm looking to implement "save" functionality, so this is not what I'm looking for.

So I'm trying to understand the different behavior: primitive vs. object and why the object updates immediately and also, what would be the proper way to implement the save function with the object. I'm aware you can use events and I could $broadcast and event on $rootScope and use that event on the second controller to assign the service property to a scope var, but I like the simplicity of assigning the service to the scope in the second controller and would like to use that method if possible.

Here is a simplified example.

var myApp = angular.module('myApp', []);

myApp.service('myService', function () {
    this.text = '';
    this.objText = {};

    this.setText = function (text) {
        this.text = text;
    };

    this.setObjText = function (obj) {
        this.objText = obj;
    };
});

myApp.controller('InputCtrl', ['$scope', 'myService', function ($scope, myService) {
    $scope.textObj = {};
    $scope.saveText = function (text) {
        myService.setText(text);
    }
    $scope.saveObj = function (obj) {
        myService.setObjText(obj);
    }
}]);

myApp.controller('OutputCtrl', ['$scope', 'myService', function ($scope, myService) {
    $scope.myService = myService;
}]);

And in the view (partial):

<div ng-controller="OutputCtrl">
    <strong>Text:</strong> {{ myService.text }}<br>
    <strong>Obj Text:</strong> {{ myService.objText }
</div>

Complete fiddle here: http://jsfiddle.net/anpsince83/uRH93/2/

役に立ちましたか?

解決

This has nothing to do with Angular. It is plain, old JavaScript-related.

In your fiddle's HTML you have (numbers are for reference below):

(1)<input type="text" ng-model="text1">
(2)<button ng-click="saveText(text1)">Save Text</button>
   ...
(3)<input type="text" ng-model="textObj.text1">
(4)<input type="text" ng-model="textObj.text2">
(5)<button ng-click="saveObj(textObj)">Save Object Text</button>

The 'string' case (primitive):

Line (1) creates a string variable, named text1, in InputCtrl's scope (hereafter inScope) and binds it to the value of the input field. Whenever the value of the input field changes, so does the value of inScope.text1.
When the button (line (2)) is clicked, myService's text variable is set to the value of inScope.text1. Because 'string' is a primitive, it is passed by value, meaning that myService.text and inScope.text1 are not referencing the same object - they just "happen" to have the same value after the saveText() method is called. Once inScope.text1 changes (e.g. the user types something in the input field), myService.text knows nothing about it. This case it equivalent to:

var a = 'value1';
var b = a;
// Now: a === 'value1', b === 'value1'

a = 'value2';
// Now: a === 'value2', b === 'value1'

The 'object' case (object):

Lines (3) and (4) create two properties (text1, text2) in the (initially empty) object referenced by inScope's textObj variable. Whenever the value of each input field changes, so does the value of the corresponding property of the object referenced by inScope.textObj.
When the button (line (5)) is clicked, myService's object variable is set to reference the same object as inScope.textObj. You noticed I use the term reference instead of value. Because 'object' is not a primitive, it is passed by reference, meaning that myService.object and inScope.textObj are referencing the same object (after the saveObj() method is called. Changing the values of the input fields defined in lines (2) and (3) affects the properties of the object referenced by inScope.textObj (which is also referenced by myService.object). This case it equivalent to:

var a = { key: 'value1' };   // Creates new object, say {obj1}
var b = a;                   // Now b references {obj1} as well
// Now: a.key === 'value1', b.key === 'value1'

a.key = 'value2';   // Changes {obj1}'s key's value
                    // but {obj1} is also referenced by b, so...
// Now: a.key === 'value2', b.key === 'value2'

// This does not happen in your code, so you never lose reference to
// the same object from both `myService.object` and `inScope.textObj`
a = { key: 'value3' };   // Creates new object, say {obj2}
                         // but b still references {obj1}, not {obj2}, so...
// Now: a.key === 'value3', b.key = 'value2'

More info on JS primitives vs objects

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top