问题
I have a pretty simple example
(also available on this fiddle: http://jsfiddle.net/depov5w6/)
I have 2 factories, one which returns an object with the property 'data', (which is bound to the scope of a controller) and another factory that has 2 methods: populateData and pushData. populateData sets the value of dataSource.data to be a new value. pushData pushes a new value onto dataSource.data
angular.module("app",[])
.factory('dataSource', function() {
return {
data: ["Init Data"]
};
})
.factory('dataModifier', function(dataSource) {
return {
populateData: function() {
dataSource.data = ["data from populateData"];
},
pushData: function() {
dataSource.data.push("new data from push data");
}
}
})
.controller('dataCtrl', function($scope, dataSource, dataModifier) {
$scope.data = dataSource.data;
$scope.populateData = dataModifier.populateData;
$scope.pushData = dataModifier.pushData;
})
In my view, I have a button that calls pushData and another button that calls populateData. I also display $scope.data
<div ng-app="app">
<div ng-controller="dataCtrl" class="container">
<p>Data: {{data}}</p>
<button ng-click="pushData()">Push Data</button>
<button ng-click="populateData()">Set Data To New Value</button>
</div>
</div>
If you look at the fiddle, if you click the Push Data button, new values get pushed onto the array. However, if you click 'Set Data To New Value', nothing happens and subsequent click to push data do nothing. I think I understand that when you click populate data, you lose the reference to the array that $scope.data was bound to, but why doesn't $scope realize that there is a new value for dataSource.data? How would I fix this? I tried making data a private variable, and using getters and setters, but that did nothing.
I appreciate any insight.
回答1:
Instead of assigning data to the scope, assign whole datasource object to the scope & then use it.
Something like: http://jsfiddle.net/depov5w6/1/
.controller('dataCtrl', function($scope, dataSource, dataModifier) {
$scope.dataSource = dataSource;
$scope.populateData = dataModifier.populateData;
$scope.pushData = dataModifier.pushData;
});
<div ng-app="app">
<div ng-controller="dataCtrl" class="container">
<p>Data: {{dataSource.data}}</p>
<button ng-click="pushData()">Push Data</button>
<button ng-click="populateData()">Set Data To New Value</button>
</div>
The reason this thing works is because of the way angular's dirty checking works. It compares whole objects to detect if there is any change. When you bind a variable to the scope it creates watch on it. If your assign object to the scope it checks object's value to monitor any changes in properties value. So you need to put watch on the property of the object.
回答2:
I think I understand that when you click populate data, you lose the reference to the array that $scope.data was bound to, but why doesn't $scope realize that there is a new value for dataSource.data?
You are correct. When your controller is created (before you have a chance to click the buttons), it points $scope.data to a list in memory that is also pointed to by dataSource.data. Your populateData()
function tells dataSource.data to point to a brand new list.. but it doesn't tell $scope.data to stop pointing to the old one.
You could use $scope.$watch()
to tell when the reference dataSource.data
changes, and then update $scope.data
:
.controller('dataCtrl', function($scope, dataSource, dataModifier) {
$scope.dataSource = dataSource;
$scope.$watch('dataSource.data', function(data) {
$scope.data = data;
});
$scope.populateData = dataModifier.populateData;
$scope.pushData = dataModifier.pushData;
})
回答3:
You have to change populateData method like below
populateData: function() {
dataSource.data. splice(dataSource.data,dataSource.data.length);
dataSource.data.push("data from populateData");
}
Demo
来源:https://stackoverflow.com/questions/25275840/modified-data-in-a-factory-thats-bound-to-a-controllers-scope-it-doesnt-upda