使用视图控制器淘汰嵌套的可观察对象

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:将实际可观察​​对象传递给另一个视图控制器?

淘汰制映射插件 - 使所有对象可观察

淘汰:为什么我的计算函数会自动将项目推送到我的可观察数组中?

LLDB – 在任意对象的实例变量上设置观察点

iOS KVO 键值观察