我们正在重构我们的客户代码并实现视图模型。我想让我们的视图模型尽可能的笨,让它们仅仅是数据表示。
我们将使用视图控制器和pub / sub根据需要获取vm的新数据,然后将数据以类似于组件在Vue中通信的方式的单向数据层次模型推入viewmodel。 / p>
对于平面viewmodel属性,这种方法可以使用“道具”功能很好地工作,但是对于嵌套的可观察对象(例如地址),我(当然)会丢失可观察对象。
var model = function() {
var self = this;
self.name = ko.observable();
self.occupation= ko.observable();
self.address = ko.observable({
street: ko.observable('Streetname'),
zip: ko.observable('Zipcode')
});
self.doUpdate = function() {
self.props({name: 'Tom', address: {street:'NewStreet'}});
};
self.props = function(data) {
var viewmodel = self;
for (p in data) {
if (self[p]) {
self[p](data[p]);
}
}
};
}
ko.applyBindings(new model());
我无法通过
self.props({name: 'Tom', address: {street:ko.observable('NewStreet')}});
因为我不得不假设我们只是从服务或其他模块中获取数据结构,因此管理可观察对象与非可观察对象是viewmodel的工作。
我想到的替代方法只是使用ko映射功能,但这需要在Props函数中提供更多的智能,在该函数中,我会做类似的事情
if(self['mapping_' + p]){
//If self.mapping_address() exists, use that to
//create mapped observables...
}else if(self[p]){}...
我会对此感到满意,但似乎有些不愉快。从服务或控制器传入分层数据时,是否有更好的方法来完成对嵌套可观察对象的维护?
答案 0 :(得分:0)
我认为您在一个小库中包装了自己的ajax(get / post)实现,我们要做的是我们有自己的xhrObject(jqXHR)返回一个promise,在我们解决该promise的延期之前,我们可以选择是否将属性更改为可观察属性,以使viewmodel实现不必处理转换每个api调用。
这里有点麻烦,希望对您有所帮助
//fakedata
var $stub = {
id: 'foo',
name: 'bar',
complex: {
name: 'test'
}
};
//fakeapi
var $fakeAsync = function(api, result) {
var dfd = $.Deferred(function() {
setTimeout(function() {
dfd.resolve(result);
}, 100);
});
return dfd.promise();
};
//ajaxlib
var $ajaxlib = new function() {
var self = this;
//normal json object
self.getAsJson = function(api) {
return $fakeAsync(api, $stub);
};
//everything changed to observables before returning
self.getAsObservable = function(api) {
var dfd = $.Deferred();
$fakeAsync(api, $stub).done(function(result) {
var propNames = [];
for (var prop in result) {
propNames.push(prop);
}
mappedResult = ko.mapping.fromJS(result);
$.each(propNames, function(index, propName) {
if (_.isObject(mappedResult[propName]) && !_.isFunction(mappedResult[propName])) {
var obj = mappedResult[propName];
mappedResult[propName] = ko.observable(obj);
}
});
dfd.resolve(mappedResult);
});
return dfd;
};
};
//viewmodel
ko.applyBindings(() => {
var self = this;
self.json = ko.observable();
self.obse = ko.observable();
self.init = function() {
$ajaxlib.getAsJson('/api/fake/1').done((result) => {
self.json(result)
});
$ajaxlib.getAsObservable('/api/fake/1').done((result) => {
self.obse(result)
});
};
self.init();
});
div {
padding: 5px;
border: 1px solid #555;
margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div>
<!-- ko with: json -->
<!-- ko with: complex -->
json: <span data-bind="text: name"></span>
<br />(isObservable: <span data-bind="text: ko.isObservable(name)"></span>)
<br /><input type="text" data-bind="textInput: name" />
<!-- /ko -->
<!-- /ko -->
</div>
<div>
<!-- ko with: obse -->
<!-- ko with: complex -->
observable: <span data-bind="text: name"></span>
<br />(isObservable: <span data-bind="text: ko.isObservable(name)"></span>)
<br /><input type="text" data-bind="textInput: name" />
<!-- /ko -->
<!-- /ko -->
</div>