我知道有几次曾经问过这个问题,但我仍然无法找到解决这个问题的办法。 (我2周前才开始使用KO。)
我有一堆字段,每个字段都绑定到我的VM中的observable。当用户单击GO按钮时,我想将输入的所有字段转换为JSON字符串并发送到服务器。这是ViewModel:
function ViewModel() {
var self = this;
function searchVar() {
self.users = ko.observable();
self.fromdate = ko.observable();
self.todate = ko.observable();
self.eqpname = ko.observable();
self.maker = ko.observable();
self.model = ko.observable();
self.makercode = ko.observable();
self.partname = ko.observable();
self.vendor = ko.observable();
}
self.searchVars = ko.observableArray();
self.addSearchVar = function (vm) {
self.searchVars.removeAll();
self.searchVars.push(vm);
//console.log(vm);
var data = ko.toJSON(self.searchVars);
alert(data);
};
var sv = new searchVar(); //not really using this
}
var vm = new ViewModel();
这里的问题是addSearchVar函数再次创建了对searchVars数组的引用,这就是创建循环引用错误的原因(我说过这个吗?)。有一个更好的方法吗? (尝试使用嵌套模型,但不能完全绕过它)。
标记非常标准。 data-bind:"value: [fieldname]"
等等。我将ko.applyBindings(vm)
放在页面底部,因为我有很多动态创建的组件。按钮属性为:<input type="button" id="btnGo" value="Go" style="background-color:#CCFFCC" data-bind="click: addSearchVar" />
欣赏任何见解。谢谢。 / AJ
答案 0 :(得分:0)
您可以通过在json编码viewmodel之前删除/替换循环引用来防止这种情况发生。一种方法是使用Douglas'JSON.decycle
:
if (typeof JSON.decycle !== 'function') {
JSON.decycle = function decycle(object) {
'use strict';
// Make a deep copy of an object or array, assuring that there is at most
// one instance of each object or array in the resulting structure. The
// duplicate references (which might be forming cycles) are replaced with
// an object of the form
// {$ref: PATH}
// where the PATH is a JSONPath string that locates the first occurance.
// So,
// var a = [];
// a[0] = a;
// return JSON.stringify(JSON.decycle(a));
// produces the string '[{"$ref":"$"}]'.
// JSONPath is used to locate the unique object. $ indicates the top level of
// the object or array. [NUMBER] or [STRING] indicates a child member or
// property.
var objects = [], // Keep a reference to each unique object or array
paths = []; // Keep the path to each unique object or array
return (function derez(value, path) {
// The derez recurses through the object, producing the deep copy.
var i, // The loop counter
name, // Property name
nu; // The new object or array
// typeof null === 'object', so go on if this value is really an object but not
// one of the weird builtin objects.
if (typeof value === 'object' && value !== null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof String)) {
// If the value is an object or array, look to see if we have already
// encountered it. If so, return a $ref/path object. This is a hard way,
// linear search that will get slower as the number of unique objects grows.
for (i = 0; i < objects.length; i += 1) {
if (objects[i] === value) {
return {$ref: paths[i]};
}
}
// Otherwise, accumulate the unique value and its path.
objects.push(value);
paths.push(path);
// If it is an array, replicate the array.
if (Object.prototype.toString.apply(value) === '[object Array]') {
nu = [];
for (i = 0; i < value.length; i += 1) {
nu[i] = derez(value[i], path + '[' + i + ']');
}
} else {
// If it is an object, replicate the object.
nu = {};
for (name in value) {
if (Object.prototype.hasOwnProperty.call(value, name)) {
nu[name] = derez(value[name],
path + '[' + JSON.stringify(name) + ']');
}
}
}
return nu;
}
return value;
}(object, '$'));
};
}
https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
这将使用参考值替换所有循环引用。
在你的情况下,你会像这样使用它:
var data = JSON.stringify(JSON.decycle(ko.toJS(self.searchVars)));
另一个选择是删除所有的圆形引用:
JSON.stringifyOnce = function (obj, replacer, space) {
var cache = [];
var json = JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// circular reference found, discard key
return;
}
// store value in our collection
cache.push(value);
}
return replacer ? replacer(key, value) : value;
}, space);
cache = null;
return json;
};
然后:
var data = JSON.stringifyOnce(ko.toJS(self.searchVars));