使用 KnockBack ViewModel,有没有一种方法可以从底层 Backbone 模型的方法创建一个计算的可观察对象?

Using a KnockBack ViewModel, is there a way to create a computed observable from the underlying Backbone model's methods?

使用 KnockBack ViewModel,有没有一种方法可以从底层 Backbone 模型的方法创建一个计算可观察值?

例如,在javascript中:

var MyModel = Backbone.model.extend({
        validate: function () {
            return this.get('name').length < 0;
        }
    }),
    baseModel = new MyModel({name: 'foo'}),
    kbViewModel = kb.viewModel(baseModel),
    modelContainer = document.querySelector('#myModel');
ko.applyBindings(kbViewModel, modelContainer);

并在 Knockout 标记中:

<div id="myModel">
    <div data-bind="css:{'invalid': !validate()}">
        <input type="text" data-bind="value: name" />
    </div>
</div>

当我尝试 运行 时,出现错误:

Unable to process binding "css: function (){return {'invalid':!validate()} }"
Message: validate is not defined

我做错了什么,还是我需要手动在 ViewModel 中创建可观察对象?

var MyModel = Backbone.Model.extend({
        validate: function () {
            return this.get('name').length > 0;
        }
    }),
    MyKBViewModel = kb.ViewModel.extend({
        constructor: function (model) {
            kb.ViewModel.prototype.constructor.call(this, model);
            this.validate = ko.pureComputed(function () {
                return this.name().length > 0;
            }, this);
        }
    }),
    baseModel = new MyModel({name: 'foo', class: 'bar'}),
    kbViewModel = new MyKBViewModel(baseModel),
    modelContainer = document.querySelector('#myModel');

ko.applyBindings(kbViewModel, modelContainer);

jsfiddles: without observable, with observable

一点序言:Knockback 将模型置于 MVVM 中,而 Knockout 实际上只是 VVM。 Knockback 还为您提供了它们之间的一些自动同步,这很好。但是您仍然需要记住,模型和视图模型是应用程序的两个不同部分。 viewmodel 不仅仅是 Bootstrap 模型的 Knockout 副本。不要在模型中放置 viewmodel 片段。

因此您需要确定验证是视图模型行为还是模型行为。我说 viewmodel,因为你想在视图中使用它。所以从模型中删除它并定义一个计算。

但是,如果你想让它成为模型的一部分,你会想要定义一个属性,而不仅仅是一个方法,以及一个模型事件处理程序来更新变化的属性。 Knockback 会尽职尽责地将属性复制到视图模型中,您可以在那里使用它。

var MyModel = Backbone.Model.extend({
    validate: function() {
      this.set('isValid', this.get('name').length > 0);
    },
    initialize: function() {
      this.validate();
      this.on('change:name', this.validate);
    }
  }),
  baseModel = new MyModel({
    name: 'foo',
    isValid: null
  }),
  kbViewModel = kb.viewModel(baseModel);

kbViewModel.validate = ko.observable();

modelContainer = document.querySelector('#myModel');

ko.applyBindings(kbViewModel, modelContainer);
.invalid {
  background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.2/backbone.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockback/1.0.0/knockback.min.js"></script>
<div id="myModel">
  <div data-bind="css:{'invalid': !isValid()}">
    <input type="text" data-bind="value: name" />
  </div>
</div>