нокаутирование вложенных наблюдаемых с помощью контроллера вида - PullRequest
0 голосов
/ 19 мая 2019

Мы проводим рефакторинг нашего клиентского кода и внедряем модели представления. Я бы хотел, чтобы наши view-модели были настолько тупыми, насколько это возможно, чтобы они были исключительно представлениями данных.

Мы будем использовать контроллер представления и pub / sub для получения свежих данных для vm по мере необходимости, и просто поместим данные в viewmodel в модели односторонней иерархии данных, схожей с тем, как компоненты взаимодействуют в Vue.

Для плоских свойств viewmodel этот подход прекрасно работает, используя функцию 'Props', но для случая вложенных наблюдаемых, таких как address, я теряю наблюдаемую (конечно).

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 mapping, но для этого требовалось немного больше интеллекта в моей функции Props, где я делал бы что-то вроде

if(self['mapping_' + p]){
  //If self.mapping_address() exists, use that to 
  //create mapped observables...
}else if(self[p]){}...

Мне было бы удобно с этим, но это кажется немного грязным. Есть ли лучший способ обеспечить поддержание вложенных наблюдаемых при передаче иерархических данных из службы или контроллера?

1 Ответ

0 голосов
/ 21 мая 2019

Полагаю, у вас есть собственная реализация ajax (get / post), заключенная в небольшую библиотеку, мы делаем то, что у нас есть собственный xhrObject (jqXHR), который возвращает обещание, прежде чем мы решим отложенное для этого обещания, мы можем выберите, если мы изменим свойства на наблюдаемые или нет, чтобы реализация 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>
...