使用视图控制器淘汰嵌套的可观察对象
Posted
技术标签:
【中文标题】使用视图控制器淘汰嵌套的可观察对象【英文标题】:knockout nested observables using view controller 【发布时间】:2019-10-06 03:27:20 【问题描述】:我们正在重构我们的客户端代码并实现视图模型。我想让我们的视图模型尽可能地愚蠢,让它们完全是数据表示。
我们将根据需要使用视图控制器和 pub/sub 为 vm 获取新数据,然后将数据以类似于 Vue 中组件通信方式的单向数据层次模型中的方式推送到视图模型中。
对于平面视图模型属性,这种方法可以正常工作,使用“道具”功能,但对于像地址这样的嵌套可观察对象的情况,我会丢失可观察对象(当然)。
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'));
因为我必须假设我们只是从服务或其他模块获取数据结构,而视图模型的工作是管理什么是可观察的。
我想到的替代方案只是使用 ko 映射功能,但这需要在我的 Props 函数中增加一点智能,我会在其中执行类似的操作
if(self['mapping_' + p])
//If self.mapping_address() exists, use that to
//create mapped observables...
else if(self[p])...
我会对此感到满意,但它似乎有点笨拙。从服务或控制器传入分层数据时,是否有更好的方法来维护嵌套的可观察对象?
【问题讨论】:
【参考方案1】:我认为你有自己的 ajax(get/post) 实现,包装在一个小库中,我们要做的是我们有自己的 xhrObject(jqXHR) 返回一个承诺,在我们解决该承诺的延迟之前,我们可以选择是否将属性更改为可观察对象,以便视图模型实现不必处理转换每个 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>
【讨论】:
以上是关于使用视图控制器淘汰嵌套的可观察对象的主要内容,如果未能解决你的问题,请参考以下文章
RxSwift:将实际可观察对象传递给另一个视图控制器?