使用视图控制器剔除嵌套的可观察对象

时间:2019-05-19 15:45:30

标签: javascript knockout.js architecture observable knockout-mapping-plugin

我们正在重构我们的客户代码并实现视图模型。我想让我们的视图模型尽可能的笨,让它们仅仅是数据表示。

我们将使用视图控制器和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]){}...

我会对此感到满意,但似乎有些不愉快。从服务或控制器传入分层数据时,是否有更好的方法来完成对嵌套可观察对象的维护?

1 个答案:

答案 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>