问题
I used ng-resource to get data from my server and then place the data into a table grid like this:
<div ng-form name="grid">
<button type="submit" data-ng-disabled="grid.$pristine">Save</button>
<div class="no-margin">
<table width="100%" cellspacing="0" class="form table">
<thead class="table-header">
<tr>
<th>ID</th>
<th>Title</th>
</tr>
</thead>
<tbody class="grid">
<tr data-ng-repeat="row in grid.data">
<td>{{ row.contentId }}</td>
<td><input type="text" ng-model="row.title" /></td>
</tr>
</tbody>
</table>
</div>
</div>
Is there a way that I can make it so that clicking on the Submit button checks through the grid for the rows that changed and then calls a putEntity(row)
function with the row as an argument?
回答1:
You could do it a few ways, and remember every NgModelController has a $dirty flag which can use to check if the input has changed. But I would say the easiest way is just to do this:
Edit to HTML:
<input type="text" ng-model="row.title" ng-change="row.changed=true" />
<button ng-click="save()">Save</button>
In JS:
$scope.save = function () {
// iterate through the collection and call putEntity for changed rows
var data = $scope.grid.data;
for (var i = 0, len = data.length; i < len; i++) {
if (data[i].changed) {
putEntity(data[i]);
}
}
}
回答2:
Here's something that could work. It is built with the JSFiddle from the first comment as a basis.
First, I changed the data-ng-disabled
attribute to changes.length <= 0
and added $scope.changes = []
to the controller.
$scope.changes = [];
Then I added a watch on $scope.data
$scope.$watch('data', function(newVal, oldVal){
for(var i = 0; i < oldVal.length; i++){
if(!angular.equals(oldVal[i], newVal[i])){
console.log('changed: ' + oldVal[i].name + ' to ' + newVal[i].name);
var indexOfOld = $scope.indexOfExisting($scope.changes, 'contentId', newVal[i].contentId);
if(indexOfOld >= 0){
$scope.changes.splice(indexOfOld, 1);
}
$scope.changes.push(newVal[i]);
}
}
}, true); // true makes sure it's a deep watch on $scope.data
Basically this runs through the array and checks if anything has changed using angular.equals. If an object has changed it is checked if it exists in $scope.changes
already. If it does, it's removed. After that newVal[i]
is pushed to $scope.changes
The $scope.indexOfExisting
is taken from this SO question
$scope.indexOfExisting = function (array, attr, value) {
for(var i = 0; i < array.length; i += 1) {
if(array[i][attr] === value) {
return i;
}
}
};
Finally I made the $scope.checkChange()
look like so
$scope.checkChange = function(){
for(var i = 0; i < $scope.changes.length; i++){
console.log($scope.changes[i].name);
//putEntity($scope.changes[i])
}
$scope.changes = [];
};
This will then give you the ability to submit only the changed rows.
回答3:
I decided to do this as follows:
Here is where I get the data and then make a copy of it:
getContents: function ($scope, entityType, action, subjectId, contentTypeId, contentStatusId) {
entityService.getContents(entityType, action, subjectId, contentTypeId, contentStatusId)
.then(function (result) {
$scope.grid.data = result;
$scope.grid.backup = angular.copy(result);
$scope.grid.newButtonEnabled = true;
}, function (result) {
alert("Error: No data returned");
$scope.grid.newButtonEnabled = false;
});
},
Then later when it comes to saving I use angular.equals to compare with a backup:
$scope.saveData = function () {
var data = $scope.grid.data;
for (var i = 0, len = $scope.grid.data.length; i < len; i++) {
if (!angular.equals($scope.grid.data[i], $scope.grid.backup[i])) {
var rowData = $scope.grid.data[i]
var idColumn = $scope.entityType.toLowerCase() + 'Id';
var entityId = rowData[idColumn];
entityService.putEntity($scope.entityType, entityId, $scope.grid.data[i])
.then(function (result) {
angular.copy(result, $scope.grid.data[i]);
angular.copy(result, $scope.grid.backup[i]);
}, function (result) {
alert("Error: " + result);
})
}
}
$scope.grid.newButtonEnabled = true;
$scope.grid.saveButtonEnabled = false;
$scope.$broadcast('tableDataSetPristine');
}
回答4:
I did something quite similar for myself, and I used a different way, I decided to directly bind all the ng-models to the data, which let me work with the indexes if I need to check every row, but I could also decide to send the whole data to the server
Then, I just have to watch all the changes made to every index (even with big data, I just have to add/remove an integer) and store them in an array
Once finished, I can click the submit button, which will loop on every index I've touched, to check if the content is really different from the original one, and then I can do whatever I want with it (like calling a putEntity function, if I had one)
I made a fiddle based on your html code, it will be easier to play with it than with a copy/paste of the code here : http://jsfiddle.net/DotDotDot/nNwqr/1/
The main change in the HTML is this part, binding the model to the data and adding a change listener
<td><input type="text" ng-model="data[$index].title" ng-change="notifyChange($index)"/></td>
I think the javascript part is quite understandable, I log indexes while the data is modified, then, later, when I click on the submit button, it can call the right function on the modified rows only, based on the logged indexes
来源:https://stackoverflow.com/questions/17648822/how-to-save-rows-in-a-grid-that-i-made-a-change-to