【问题标题】:Knockout binding does not update model淘汰赛绑定不更新模型
【发布时间】:2016-06-21 06:02:07
【问题描述】:

我才开始使用 Knockout,但我已经体验过 MVVM 在 WPF 中的应用。我遇到的问题是包装模型对象的 Knockout 视图模型不会更新原始模型。这是一个小例子:

HTML

<select name="size" id="sel-size" data-bind="options: sizes, value: size"></select>

JS

// default settings
var settings = {
    size: 5
};

// settings view model
var settingsVM = function () {
    return {
        sizes: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        size: ko.observable(settings.size)
    }
}();

// subscribe to changes
settingsVM.size.subscribe(function() {
    alert("Model: " + settings.size + " / VM: " + settingsVM.size());
})

ko.applyBindings(settingsVM);

View in JSFiddle

更改下拉选择时,仅更新视图模型中的settingsVM.size(),但模型中的settings.size 保持不变。

似乎用引用初始化observable 并没有将该引用作为属性访问器的支持字段。我对 MVVM 淘汰赛的方式缺少什么?

【问题讨论】:

    标签: javascript mvvm knockout.js


    【解决方案1】:

    当你改变 select 的值时,它实际上覆盖了你的 size observable 的内容。如果要设置支持字段类型排列,则必须使用具有读/写功能的计算 observable。

    另外,如果您确实想更新原始的“设置”对象,则需要处理它而不是单独设置 settings.size 属性。这样的事情将使原始模型与选择保持同步:

    // settings view model
    var settingsVM = function () {
      var self=this;
      self.settings = ko.observable({
        size: 5
      });
      self.sizes =   [{size:1},{size:2},{size:3},{size:4},{size:5}];
      self.size = ko.computed({
              read:function(){
                return self.settings();
              },
              write:function(val){
                self.settings(val);
       }});
       self.size.subscribe(function() {
    
                alert("Model: " + self.settings().size + " / VM: " + self.size().size);
            });
    };
    
    ko.applyBindings(new settingsVM());
    

    JSFiddle

    【讨论】:

      【解决方案2】:

      Knockout 的肮脏小秘密在于它并不是真正的 MVVM。它没有独立于视图建模的数据建模结构,因此它只是 VVM。在普通的应用程序设计中,数据模型位于服务器上,应用程序中的“模型”是一组简单的 AJAX 例程,用于将其片段放入视图模型中。 Knockout 本身与 AJAX 无关,因此如何处理应用程序的这部分取决于您自己(但通常很简单)。

      在您的问题中,您希望模型自动更新以反映相应的视图模型元素。那是多余的;目前尚不清楚您是否需要单独的东西来从视图模式中复制一个值。 viewmodel 可以只包含模型。但是,如果您有更多模型-y 的事情要做,因此您确实需要将模型元素收集到一个结构中,Will Jenkins 建议使用 subscribe 是响应模型变化的最直接方式视图模型。

      在下面的 cmets 中,Will Jenkins 提出了 Knockout 提供的实用函数,用于在 JSON 和 observable 之间进行转换。这些也有助于在模型和视图模型之间来回迁移。

      另请参阅:ko.mapping

      【讨论】:

      • 这确实意味着 Knockout 不是“模型感知”的。一旦你从服务器中取出一个模型,通过视图模型对其进行编辑并希望将它持久化回服务器,这意味着你必须从头开始重写你的模型吗?还必须订阅代理来更新原始参考对我来说听起来是多余的。
      • 你根本不需要使用 subscribe,这有点像淘汰 imo 的反模式。关于服务器往返,这很容易——您可以直接从 json 映射到您的 vm,然后使用诸如 ko.toJSON 之类的实用程序函数对您的 vm 进行去敲除以将其发回。见这里:knockoutjs.com/documentation/json-data.html
      • @Ucodia 我想如果您不坚持遵循拥有单独模型的模式,您会发现 Knockout 更简单。您的模型在服务器上。您只需要将数据获取到您的应用中并将数据从您的应用写回服务器即可。
      • 这是一个“肮脏的小秘密”吗?除非您专门将 INPC 添加到模型层,否则 MVVM 的 WPF 风格也不是“模型感知的”,无论如何这对于某些人来说是一个极具争议的话题。在 WPF 中,VM 属性设置器通常会将更改向下传播到模型;相当于 KO 的是订阅者功能。他需要做的就是在settingsVM.size.subscribe 中更新模型,或者在提交时一次性完成所有操作。它与 WPF 并没有什么不同。
      • @RoyJ 我并不坚持遵循一种模式,这就是我问 Knockout 有何不同之处。现在,如果我将分解模型包装在视图模型中,这也意味着我必须稍后从视图模型重建模型,因为无法自动解包分解模型(您显然不会发回视图模型)。当你可以整齐地包裹起来的时候,你不需要关心这样的事情,没有任何额外的步骤,这当然不是视图模型的责任。
      【解决方案3】:

      它不更新的原因是因为您的 settings.size 不是可观察的,并且您的代码没有提供在 html 中使用 settings.size 作为选定大小。

      // default Model for settings
      function settings(){
          this.size = ko.observable(5);
      }
      
      // settings view model
      var settingsVM = function () {    
          return {
              sizes: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
              settings: new settings()
          }
      }();
      
      // subscribe to changes
      settingsVM.settings.size.subscribe(function() {
          alert("model: " + settingsVM.settings.size());
      })
      
      ko.applyBindings(settingsVM);
      

      查看更新的小提琴here

      【讨论】:

        猜你喜欢
        • 2014-10-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-11
        相关资源
        最近更新 更多