Добавление вычисляемых свойств в модель представления и ее подэлементы

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

Допустим, у меня есть модель представления, которая выглядит так:

function viewModel(){
    var self = this;
    self.foo = ko.observable();
    self.bars = ko.observableArray();
}

function bar(data){
    var self = this;
    self.baz = ko.observable();
    ko.mapping.fromJS(data, mapperSettings, self);
}

Я использую плагин ko.mapping для сопоставления некоторых данных с сервера:

var mapperSettings = {
     bars: {
        create: function(options) {
            return new bar(options.data);
        }
    }
};    

var dummyData = {
    foo: '1',
    bars: [{ baz: 1 }, { baz: 2 }]
};

var vm = new viewModel();
ko.mapping.fromJS(dummyData, mapperSettings, vm);

Пока все в порядке.

Теперь я хотел бы дополнить модель представления и ее элементы некоторыми дополнительными вычисляемыми свойствами. Мне удалось успешно добавить его в модель представления (something_computed ниже), но мне не удалось добавить вычисляемое свойство к элементу списка — свойство computed к элементу bar ниже:

var settings = {  
  create: function(options) {

    var model = ko.mapping.fromJS(options.data, {
      bars: {
        create: function (options) {
          // why is this callback called 4 times and not 2?
          var self = options.data;

          self.computed = ko.computed(function(){
              return this.baz() + ' comp';
          }, self);

          return self;
        }
      }
    });

    model.something_computed = ko.computed(function(){
      return this.foo() + '...';
    }, model);

    return model;
  }
};

var newViewModel = ko.mapping.fromJS(vm, settings);

ko.applyBindings(newViewModel);

Скрипка


person Johan    schedule 05.03.2016    source источник
comment
См. stackoverflow.com/questions/10906252/ ?   -  person Roy J    schedule 06.03.2016
comment
@RoyJ Спасибо за ссылку. Насколько я могу судить из различных потоков SO, я правильно выполняю сопоставление в своем последнем обновлении выше. Или я что-то пропустил? Я не могу понять, почему обратный вызов create вызывается более двух раз...   -  person Johan    schedule 06.03.2016
comment
Я думаю, что ваша функция создания должна создавать новый объект, а не просто изменять и возвращать то, что вы получаете. Я не знаю, почему он звонит четыре раза, но последние два раза не имеют baz, так что вы можете их закоротить. jsfiddle.net/4s6jsLx1/7   -  person Roy J    schedule 06.03.2016
comment
Да я тоже так думаю. Но несмотря на это, я не могу понять, почему его вызывают 4 раза. Спасибо хоть   -  person Johan    schedule 06.03.2016
comment
Пробовали ли вы выполнить код сопоставления в отладчике?   -  person Jeroen    schedule 06.03.2016


Ответы (1)


Я настроил вашу скрипку и заставил ее работать, как я думаю, вы этого хотите - она ​​здесь:

https://jsfiddle.net/4s6jsLx1/10/

Главное, что я сделал, это реализовать шаблон, описанный ниже, чтобы позволить вам легко и четко определять модели вложенных представлений, каждая из которых имеет вычисляемые значения, без того, чтобы ваш код завязывался в узлы (см. примечание внизу о вызове create 4 раза) --

Во-первых, создайте некоторые фиктивные данные, например

data = {foo: 1}

Затем создайте определение модели представления для модели представления, которая будет содержать эти фиктивные данные. Определите ваши вычисленные здесь. Обычно вам не нужно определять наблюдаемое для foo, потому что плагин сопоставления создаст его для вас, но поскольку вы ссылаетесь на него из вычисляемого, то в этом случае вам нужно его определить:

function viewModel() {
    var self = this
    self.foo = ko.observable()
    self.computed_foo = ko.computed(function() {
        return self.foo() + " comp"
    })
}

Затем создайте его как пустую модель представления, чтобы получить экземпляр, который вычислил:

blank_vm = new viewModel()

Наконец, используйте ko.mapping, чтобы заполнить его данными. Этот пример предоставляет {} как пустой объект настроек, но в скрипте я использую ваш правильный объект настроек для создания полос:

vm = ko.mapping.fromJS(data, {}, blank_vm)

vm теперь имеет два свойства: foo (где foo() == 1) и computed_foo (где computed_foo() == "1_comp"


Что касается вызова create 4 раза, если вы замените свой console.log на:

console.log('create', options.data);

тогда вы увидите, что при третьем и четвертом вызове переданные данные являются мусором. Я не тратил много времени на то, чтобы выяснить, откуда именно он взялся, но в целом я думаю, что ваш код стал немного запутанным с точки зрения взаимодействия сопоставлений и вычислений. В моем решении этот метод создания полностью удален, следуя шаблону, описанному выше, который предназначен для отделения создания вычислений от процесса сопоставления. Надеюсь, это делает то, что вам нужно!

person sifriday    schedule 06.03.2016
comment
Да, я тоже заметил мусорную часть. Я думаю, что основная проблема заключается в том, что, как вы подразумеваете, я использую ko.mapping.fromJS вместе с уже сопоставленной моделью представления вместо некоторых чистых данных, таких как проанализированный JSON. Довольно очевидно, если подумать. Иногда вам нужны две пары глаз ;) Спасибо за ваш описательный ответ - я очень ценю это! - person Johan; 06.03.2016
comment
Спасибо, рад, что сработало! Я считаю, что вы правы насчет причины мусора, это имеет смысл. - person sifriday; 06.03.2016