Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performing partial updates with KnockoutJS mapping plugin

Right now, I'm using this JSON with the KO Mapping plugin and it's working fine:

{
  "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"
}

Now my requirement is to perform "partial updates". Some scenarios when I'd like to do that:

  • I need to change the Data array in the second control.
  • I need to update only one Control and not the whole Page (this is the class I'm serializing, the root in this JSON).
  • I need to add another Control to my Page.

Perhaps another workaround would be recreating the original object with ko.mapping.toJS(viewModel), change it and then re-map it again... but I believe that you will come out with something better.


EDIT: I tried with ko.mapping.fromJS(updatedControl, viewModel.Controls()[0]) but it didn't work, here is my code:

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

response:

{
  "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/

like image 609
faloi Avatar asked Apr 18 '12 15:04

faloi


1 Answers

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.

like image 120
madcapnmckay Avatar answered Oct 23 '22 04:10

madcapnmckay