6

现在,我将这个 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: check it out at http://jsfiddle.net/faloi/4FcAy/10/

4

2 回答 2

6

I think this is now the single most asked question re the mapping plugin. The plugin can only currently do partial updates of properties. E.g if you were to remap the below data to your object

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

It would update just those properties. However the collections are treated differently so if you were to remap with the below.

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

It would remove the non-matching item in your Controls collection instead of updating the matching item. The only way to get this to work is to loop through the collection and map each individual item you wish to update. Your code is almost on the right track I believe it should be

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

EDIT

You were missing the middle mapping argument on the fromJS call.

http://jsfiddle.net/3CtFq/

The mapping object is usually optional but I guess if you are mapping a sub object as is your case, the plugin can't tell that this sub items parent was originally mapped with no options.

Hope this helps.

于 2012-04-18T22:16:27.547 回答
0

I thought that was the whole point of having the 'key' option. I realize there are caveats to this kind of feature, but I'm surprised there's no support for it at all.

Anyway - this is (regrettably) what I've ended up having to do to partially update a list of orders - which is to loop through and replace items.

data is my main AJAX/JSON model from the server and contains data.orders which is ko.observableArray([]) of OrderViewModel items. It also contains all kinds of other stuff data.products, data.foo that I want to initially map.

I have a mapping rule to create new OrderViewModel objects automatically during the mapping process.

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

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

When I get the data back from the server I initially want to do a complete mapping of my model. So I set initialUpdate=true.

When I want to refresh a single order I have 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);
         }
     }
于 2014-07-16T01:32:00.080 回答