【问题标题】:Performing partial updates with KnockoutJS mapping plugin使用 KnockoutJS 映射插件执行部分更新
【发布时间】:2012-04-30 02:00:38
【问题描述】:

现在,我正在将此 JSON 与 KO Mapping 插件一起使用,并且运行良好:

{
  "Controls": [
    {
      "Fields": [
        {
          "Name": "emailField", 
          "Text": "email", 
          "Visible": true
        }, 
        {
          "Name": "hiddenField", 
          "Text": "text", 
          "Visible": true
        }
      ], 
      "Name": "form2", 
      "Type": "Form"
    }, 
    {
      "Data": [
        [
          "Federico Aloi", 
          20
        ], 
        [
          "Andres Lopez", 
          31
        ], 
        [
          "Pablo Perez", 
          32
        ]
      ], 
      "Fields": [
        {
          "Name": "nameField", 
          "Text": "Nombre", 
          "Visible": true
        }, 
        {
          "Name": "ageField", 
          "Text": "Edad", 
          "Visible": true
        }
      ], 
      "Name": "datagrid1", 
      "Type": "Datagrid"
    }
  ], 
  "Name": "pagina1", 
  "Title": "Probando el KO"
}

现在我的要求是执行“部分更新”。我想这样做的一些场景:

  • 我需要更改第二个控件中的 Data 数组。
  • 我只需要更新一个控件而不是整个页面(这是我要序列化的类,这个 JSON 中的根)。
  • 我需要向我的页面添加另一个控件。

也许另一种解决方法是使用ko.mapping.toJS(viewModel) 重新创建原始对象,更改它然后重新映射它......但我相信你会得到更好的东西。


编辑:我尝试使用ko.mapping.fromJS(updatedControl, viewModel.Controls()[0]),但没有成功,这是我的代码:

function (control) {
    $.getJSON($.format('api/control/{0}/{1}', viewModel.Name(), control.Name()), function (response) {
        ko.mapping.fromJS(response, viewModel.Controls()[0]);
    });
},

回复:

{
  "Fields": [
    {
      "Name": "emailField", 
      "Text": "email", 
      "Visible": true
    }, 
    {
      "Name": "hiddenField", 
      "Text": "text", 
      "Visible": true
    }
  ], 
  "Name": "form2", 
  "Type": "Form"
}

EDIT2:查看http://jsfiddle.net/faloi/4FcAy/10/

【问题讨论】:

  • 你能用你的代码替换这个 jsfiddle 中的代码吗:jsfiddle.net/JasonMore/LKS5b 我敢打赌,一旦我看到 JS 运行,我可以帮助你。

标签: knockout.js knockout-mapping-plugin


【解决方案1】:

我认为这是现在关于映射插件的最常见问题。该插件目前只能对属性进行部分更新。例如,如果您要将以下数据重新映射到您的对象

{ "Name": "pagina1 foo", "Title": "Probando el KO bar" }

它只会更新那些属性。但是,如果您要使用以下内容重新映射,则对集合的处理方式有所不同。

{ "Name": "pagina1 foo", "Title": "Probando el KO bar", 
  Controls: [ { // snip some updated object } ] }

它将删除 Controls 集合中的不匹配项,而不是更新匹配项。让它工作的唯一方法是遍历集合并映射您希望更新的每个单独的项目。你的代码几乎是在正确的轨道上,我相信它应该是

ko.mapping.fromJS(updatedControl, viewModel.Controls()[0])

编辑

您在 fromJS 调用中缺少中间映射参数。

http://jsfiddle.net/3CtFq/

映射对象通常是可选的,但我猜如果你正在映射一个子对象,插件无法判断这个子项父项最初是在没有选项的情况下映射的。

希望这会有所帮助。

【讨论】:

  • 我尝试使用ko.mapping.fromJS(updatedControl, viewModel.Controls()[0]),但没有成功...这很奇怪,因为viewModel.Controls()[0] 实际上返回了第一个映射控件
  • @faloi - 如果您可以转移到 jsfiddle,这将使我们更容易解决问题。
  • 第二个被问到最多的问题是“这种行为会改变吗?”
【解决方案2】:

我认为这就是拥有“关键”选项的全部意义所在。我知道这种功能有一些注意事项,但我很惊讶根本不支持它。

无论如何 - 这是(遗憾的是)我最终不得不做的部分更新订单列表 - 这是循环和替换项目。

data 是我来自服务器的主要 AJAX/JSON 模型,包含 data.orders,它是 ko.observableArray([])OrderViewModel 项目。它还包含我最初想要映射的各种其他东西data.productsdata.foo

我有一个映射规则,可以在映射过程中自动创建新的OrderViewModel 对象。

            var mapping =
            {
                'orders':
                {
                    key: function (item)
                    {
                        return ko.utils.unwrapObservable(item.orderId);
                    },

                    create: function (options)
                    {
                        return new OrderViewModel(options.data);
                    }
                }
            };

当我从服务器取回数据时,我最初想对我的模型进行完整的映射。所以我设置了initialUpdate=true

当我想刷新一个订单时,我有initialUpdate=false

     if (initialUpdate) 
     {
         // standard initial mapping 
         ko.mapping.fromJS(data, mapping, this);
     }
     else
     {
         // merge orders
         var newModel = ko.mapping.fromJS(data, mapping);

         // go through all orders in the updated list
         for (var i = 0; i < newModel.orders().length; i++)
         {
             var newOrder = newModel.orders()[i];

             // find order with same key in existing ko model
             var match = ko.utils.arrayFirst(this.orders(), function (item)
             {
                 return newOrder.orderId() === item.orderId();
             });

             if (match == null)
             {
                 // TODO: insert new order                            
             }

             // replace original order in list
             this.orders.replace(match, newOrder);
         }
     }

【讨论】:

    猜你喜欢
    • 2012-07-31
    • 2013-07-02
    • 2012-08-29
    • 2012-03-06
    • 1970-01-01
    • 2012-08-12
    • 2015-07-04
    • 2011-06-12
    • 2012-03-27
    相关资源
    最近更新 更多