【问题标题】:Add computed properties to a view model and its sub items将计算属性添加到视图模型及其子项
【发布时间】:2016-06-19 14:47:12
【问题描述】:

我将尝试为我遇到的问题做一个简化的例子。我认为我已经非常接近解决方案了,但我需要你的帮助来画龙点睛。

假设我有一个看起来像这样的视图模型:

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 plugin 映射来自服务器的一些数据:

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),但是我无法将计算属性添加到列表项中——下面的bar 项上的computed 属性:

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);

Fiddle

【问题讨论】:

  • @RoyJ 感谢您的链接。据我从各种 SO 线程中可以看出,我在上面的最后一次更新中正确地进行了映射。还是我错过了什么?我无法理解为什么create 回调被调用了两次以上......
  • 我认为您应该让您的 create 函数创建一个新对象,而不仅仅是修改并返回您收到的内容。我不知道为什么它会调用四次,但最后两次没有baz,所以你可以将它们短路。 jsfiddle.net/4s6jsLx1/7
  • 是的,我也这么认为。但无论如何,我不明白为什么它被调用了 4 次。不过谢谢
  • 你试过在调试器中单步调试映射代码吗?

标签: knockout.js knockout-mapping-plugin


【解决方案1】:

我已经调整了你的小提琴并让它按照我认为你想要的方式工作 - 它就在这里:

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

我所做的主要事情是实现下面描述的模式,以便让您轻松、干净地定义嵌套视图模型,每个模型都带有计算,而您的代码不会被打结(请参阅底部关于调用 create 的注释4次)--

首先,创建一些虚拟数据,例如

data = {foo: 1}

然后,为将保存此虚拟数据的视图模型创建一个视图模型定义。在此处定义您的计算。您通常不需要为 foo 定义一个 observable,因为映射插件会为您创建它,但是因为您从计算中引用它,所以在这种情况下,您确实需要定义它:

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);

那么你会在第3次和第4次调用时看到,传入的数据是垃圾。我没有花太多时间弄清楚它的确切来源,但从广义上讲,我认为你的代码在映射和计算如何交互方面变得有点纠结。我的解决方案完全按照上面概述的模式删除了这个创建方法,该模式旨在将计算的创建与映射过程分开。希望它能满足您的需求!

【讨论】:

  • 是的,我也注意到了“垃圾部分”。我认为主要问题是,正如您所暗示的那样,我使用 ko.mapping.fromJS 以及已经映射的视图模型,而不是像解析的 JSON 这样的“干净”数据。当您考虑它时,非常明显。有时您需要两只眼睛;)感谢您的描述性回答-我真的很感激!
  • 谢谢,很高兴成功了!我认为你对垃圾的原因是正确的,这是有道理的。
猜你喜欢
  • 2012-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多