我从服务器获取了一个对象列表
[{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:30}]
我将它们加载到html控件中供用户编辑。 然后有一个按钮可以将整个列表批量保存回数据库。
我只想发送已更改的对象子集,而不是发送整个列表。
它可以是数组中的任意数量的项目。我想做类似于像Angular这样的框架,它在没有对它进行任何更改时标记像“pristine”这样的对象属性。然后使用该标志仅向服务器发布不是“原始”的项目,即已修改的项目。
答案 0 :(得分:10)
下面是一个函数,当提供旧的对象数组和一个新的对象数组时,它将返回一个已更改对象的数组:
getChanges = function(oldArray, newArray) {
var changes, i, item, j, len;
if (JSON.stringify(oldArray) === JSON.stringify(newArray)) {
return false;
}
changes = [];
for (i = j = 0, len = newArray.length; j < len; i = ++j) {
item = newArray[i];
if (JSON.stringify(item) !== JSON.stringify(oldArray[i])) {
changes.push(item);
}
}
return changes;
};
例如:
var older = [{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:30}]
var newer = [{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:20}]
getChanges(older, newer)
(注意test03的年龄现在是20)将返回
[{name:'test03', age:20}]
您只需要导出客户端编辑值的完整列表,将其与旧列表进行比较,然后将更改列表发送到服务器。
希望这有帮助!
答案 1 :(得分:3)
以下是一些想法。
使用框架。你谈到了Angular。
使用Proxies,但Internet Explorer不支持。
可以使用Object.defineProperty
的set / get来实现某种变更跟踪,而不是使用经典属性。
使用getter / setting函数存储数据而不是属性:getName()
和setName()
。虽然这是defineProperty
现在所做的旧方法。
每当您将数据绑定到表单元素时,请设置一个特殊属性,指示属性是否已更改。像__hasChanged
这样的东西。如果对象上的任何属性发生更改,则设置为true。
旧学校强力方式:保留原始服务器数据列表,深入复制到另一个列表,将表单控件绑定到新列表,然后当用户单击提交时,比较对象在原始列表中新建列表中的对象,随时删除已更改的对象。可能是最简单的,但不一定是最干净的。
对#6的另一种看法:将特殊属性附加到始终返回对象原始版本的每个对象:
var myData = [{name: "Larry", age: 47}];
var dataWithCopyOfSelf = myData.map(function(data) {
Object.assign({}, data, { original: data });
});
// now bind your form to dataWithCopyOfSelf.
当然,这个解决方案假定了一些事情:(1)你的对象是扁平而简单的,因为Object.assign()没有深度复制,(2)你的原始数据集永远不会改变,并且( 3)没有任何东西触及original
的内容。
那里有很多解决方案。
答案 2 :(得分:1)
完成此任务:拦截对象写入,并将其标记为脏。
代理允许创建一个 处理程序 对象,该对象可以捕获,进行操作,然后转发更改原始的 target 对象,基本上可以重新配置其行为。
我们要用于拦截对象写入的陷阱是handler set()。
此时,我们可以添加不可枚举的属性 标志,例如:_isDirty
使用Object.defineProperty()将我们的对象标记为已修改, 肮脏。
使用陷阱(在我们的示例中为处理程序的set()
)时,不会应用更改或将更改反映到对象,因此我们需要转发参数值使用 Reflect.set() 到 target 对象。
最后,要检索修改后的对象,请使用我们的代理对象 filter()
查找那些拥有own Property {{1 }}。
"_isDirty"
// From server:
const dataOrg = [
{id:1, name:'a', age:10},
{id:2, name:'b', age:20},
{id:3, name:'c', age:30}
];
// Mirror data from server to observable Proxies:
const data = dataOrg.map(ob => new Proxy(ob, {
set() {
Object.defineProperty(ob, "_isDirty", {value: true}); // Flag
return Reflect.set(...arguments); // Forward trapped args to ob
}
}));
// From now on, use proxied data. Let's change some values:
data[0].name = "Lorem";
data[0].age = 42;
data[2].age = 31;
// Collect modified data
const dataMod = data.filter(ob => ob.hasOwnProperty("_isDirty"));
// Test what we're about to send back to server:
console.log(JSON.stringify(dataMod, null, 2));
如果由于某种原因您不愿意进入原始对象,添加额外的属性作为标志,则可以立即填充
.defineProperty()
(带有修改对象的数组)的引用:
dataMod
答案 3 :(得分:0)
无需了解原型属性,只要表单控件元素检测到更改,就可以将它们存储在另一个数组中
有些事情:
var modified = [];
data.forEach(function(item){
var domNode = // whatever you use to match data to form control element
domNode.addEventListener('input',function(){
if(modified.indexOf(item) === -1){
modified.push(item);
}
});
});
然后在保存时将modified
数组发送到服务器
答案 4 :(得分:0)
以下是如何跟踪记录已更改的角度示例。您可以通过增强对象来更加增强跟踪实际字段和值。这是一个例子:
<强> App.js 强>
(function () {
angular.module('myApp', [])
.controller('myController', function ($scope) {
$scope.data = [{ id: 1, name: 'test01', age: 10 }, { id: 2, name: 'test02', age: 20 }, { id: 3, name: 'test03', age: 30 }];
$scope.isDirty = { changes: false, changedIds: [] };
$scope.submit = function () {
console.log($scope.isDirty);
}
})
.directive('ngChangeListener', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.change(function (e) {
var recordId = $(e.currentTarget).attr('data-id');
scope.isDirty.changes = true;
scope.isDirty.changedIds.push(recordId);
})
}
}
})
}());
<强> HTML 强>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AngularJSFiddle.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="myApp">
<head runat="server">
<title></title>
</head>
<body ng-controller="myController">
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in data">
<td><input ng-change-listener type="text" data-id="{{item.id}}" value="{{item.name}}" /></td>
<td><input ng-change-listener type="text" data-id="{{item.id}}" value="{{item.age}}" /></td>
</tr>
</tbody>
</table>
<div>{{isDirty.changes}}</div>
<button type="button" ng-click="submit()">Submit</button>
</div>
<script src="scripts/jquery.js"></script>
<script src="scripts/angular.js"></script>
<script src="scripts/app.js"></script>
</body>
</html>
一旦你建立了'跟踪更改'对象,那么只需更新正确的记录(应与ID一致),而不是进行批量更新。
答案 5 :(得分:0)
为什么不使用Ember.js可观察属性?您可以使用Ember.observer函数来获取和设置数据中的更改。
Ember.Object.extend({
valueObserver: Ember.observer('value', function(sender, key, value, rev) {
// Executes whenever the "value" property changes
// See the addObserver method for more information about the callback arguments
})
});
Ember.object实际上为你做了很多繁重的工作。
定义对象后,添加如下的观察者:
object.addObserver('propertyKey', targetObject, targetAction)
答案 6 :(得分:0)
我的想法是对对象键进行排序并将对象转换为要比较的字符串:
// use this function to sort keys, and save key=>value in an array
function objectSerilize(obj) {
let keys = Object.keys(obj)
let results = []
keys.sort((a, b) => a > b ? -1 : a < b ? 1 : 0)
keys.forEach(key => {
let value = obj[key]
if (typeof value === 'object') {
value = objectSerilize(value)
}
results.push({
key,
value,
})
})
return results
}
// use this function to compare
function compareObject(a, b) {
let aStr = JSON.stringify(objectSerilize(a))
let bStr = JSON.stringify(objectSerilize(b))
return aStr === bStr
}
这就是我的想法。
答案 7 :(得分:0)
这是最干净的方法,我认为在添加或删除或修改属性时,让对象发出事件。 一个简单的实现可能涉及一个带有对象键的数组。每当构造函数的setter或heck构造函数返回此值时,它都会首先调用一个静态函数返回一个promise;解析:使用数组中更改后的值进行映射:添加的内容,删除的内容或两者都不显示。这样一个人可以得到(“改变”)等等。返回一个数组。 同样,每个设置器都可以发出带有初始值和新值参数的事件。 假设使用了类,则可以在父泛型类中轻松地通过其构造函数调用静态方法,因此实际上您可以通过将对象传递给自身或通过super(checkMeProperty)传递给父类来简化大多数方法