Knockout.js 使用敲除映射插件在ViewModel的一部分中添加和更新观测值-不工作

Knockout.js 使用敲除映射插件在ViewModel的一部分中添加和更新观测值-不工作,knockout.js,knockout-mapping-plugin,Knockout.js,Knockout Mapping Plugin,虽然这看起来像是关于淘汰映射插件和部分视图模型更新的许多类似问题,但在我所有的搜索中,我没有看到我在这里面临的问题 下面我将用一个例子来详细说明,但简而言之,这就是我要做的 首先我使用KO-Mapping插件的KO.Mapping.fromJS从Javascript对象创建视图模型。 现在,我只想用从服务器接收的数据更新视图模型的一部分。所以 我使用ko.mapping.toJS 使用来自服务器的数据更新JS对象的部分 使用更新的JS对象,使用ko.mapping.fromJS 问题 通过映射从

虽然这看起来像是关于淘汰映射插件和部分视图模型更新的许多类似问题,但在我所有的搜索中,我没有看到我在这里面临的问题

下面我将用一个例子来详细说明,但简而言之,这就是我要做的

首先我使用KO-Mapping插件的
KO.Mapping.fromJS
从Javascript对象创建视图模型。 现在,我只想用从服务器接收的数据更新视图模型的一部分。所以

  • 我使用
    ko.mapping.toJS
  • 使用来自服务器的数据更新JS对象的部分
  • 使用更新的JS对象,使用
    ko.mapping.fromJS
  • 问题

  • 通过映射从JS对象首次创建时的可观察值即使在更新后也保持不变
  • 如果服务器数据对象中存在新属性,则这些属性将显示它们所包含的值 在更新的视图模型中随附,但在随后的更新中不会更新为新值
  • 这里是一个简化的例子

    视图

    <div>
      <label data-bind="text: mainTitle"></label>
      <div data-bind="with: layer0">
        <label>Name - </label>
        <label data-bind="text: name"></label>
        <label>Type - </label>
        <label data-bind="text: type"></label>
      </div>
    </div>
    
    此JS对象
    dataModel

    var viewModel = ko.mapping.fromJS(dataModel);
    
    并通过applyBindings应用于上面的视图,并按预期显示数据

    现在我从服务器接收这个JS对象

    var newJSobjectFromServer =  {
             name: "item2Name",
             description: "item2Description"   // new property that was not on first model
             type: "type2"
            }
    
    现在我做不到

    viewModel.layer0.itemDetails(newJSobjectFromServer);
    
    因为在映射插件
    itemDetails
    创建的
    viewModel
    中,它是一个属性,而不是一个方法,只有
    name
    type
    是方法

    所以我首先从viewModel中提取一个JS对象

    var viewModelJS = ko.mapping.toJS(viewModel);
    
    然后我用新的服务器数据更新了部分
    viewModelJS

    viewModelJS.layer0.itemDetails = newJSobjectFromServer;
    
    因此
    viewModelJS
    成为

    {
     mainTitle: "Main Title",
     layer0:{
              itemDetails:{
                           name: "item2Name",
                           description: "item2Description"   // new property 
                           type: "type2"
                          }, 
              otherDetails:{ 
                             prop1: "prop1Value",
                             prop2: "prop2Value"
                           }
            }
    }
    
    然后,我尝试通过以下方式更新viewModel

    ko.mapping.fromJS(viewModelJS , viewModel);
    
    也试过

    ko.mapping.fromJS(viewModelJS , {}, viewModel);
    
    但结果是

    layer0.itemDetails.name()
    仍然是
    “item1”
    layer0.itemDetails.type()
    仍然是
    “type1”
    ,而新的可观察的
    layer0.itemDetails.description()仍然存在并且具有值“item2Description”

    现在如果我用另一个服务器返回的对象重复更新

    {
       name: "item3Name",
       description: "item3Desctiption" 
       type: "type3"
    }
    
    这次
    viewModel
    中的所有值与上次相同
    layer0.itemDetails.name()
    仍然是
    “item1”
    layer0.itemDetails.type()
    仍然是
    “type1”
    layer0.itemDetails.description()
    仍然是
    “item2Description”
    ,因此视图中没有任何更改


    处理的对象的复杂性使得我有必要使用映射插件。虽然示例简化了,但它完全描述了我面临的问题。我真的想找个人来指导我如何在上述场景中使用服务器的数据更新viewModel的这一部分。如果这个问题解决了,我可以将它应用到我所面临的更复杂的现实世界场景中。提前感谢。

    您的UI仍然绑定到同一个ViewModel,再次运行映射不会刷新它

    您可以使用不同的方法创建父视图模型,并将“viewModel”设置为父视图模型的可观察成员。看看这个提琴,看看这个行为的一个非常匆忙(但有效)的例子

    此外,如果GetNewData()函数进行ajax调用以获取数据,这仍然会刷新您的UI—这就是我使用此“模式”的方式

    更新

    一些想法:1)重新构造数据,使其在所有情况下都是统一的。否则,您将以非常不同的方式构建每个ViewModel,这将导致以后的可读性问题。

    2) 如果无法重新构造数据,请放弃映射插件并手动执行所有操作。我想说,这会产生更清晰的标记,并使您能够更好地控制数据。

    非常感谢您的建议,在现实世界中,我认为我无法克服一些障碍。我用叉子叉了你的小提琴。1) 第一个视图模型本身需要从JS对象进行解释——尽管注入子视图模型可能是可行的2)注意,从服务器返回的对象并不是整个结构3)最重要的是,从fiddle中可以看出,引用$root或$parent的其他元素存在于由上下文绑定到itemDetails的元素中因此,子视图模型仍然有效。请参阅我的更新。你可能想放弃这个映射插件
    {
       name: "item3Name",
       description: "item3Desctiption" 
       type: "type3"
    }
    
    var parentViewModel = function (data) {
        var _self = this;
        _self.Data = ko.observable('');
        _self.LoadData = function () {
            _self.Data(new viewModel(data));
        };
        _self.GetNewData = function (data, item) {
            _self.Data(new viewModel(newJSobjectFromServer));
        };
        _self.LoadData();
    };
    var viewModel = function (data) {
        var _self = this;
        ko.mapping.fromJS(data, {}, _self);
        _self.Data = ko.observable(data);
    };
    ko.applyBindings(new parentViewModel(dataModel));