Performing partial updates using the KnockoutJS mapping plugin

Right now, I am using this JSON with the KO Mapping plugin and it works great:

{ "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 do "partial updates." Some scenarios when I would like to do this:

  • I need to change the data array in the second control.
  • I need to refresh 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 to recreate the original object using 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 that 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]); }); }, 

Answer:

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

EDIT2: check out http://jsfiddle.net/faloi/4FcAy/10/

+6
source share
2 answers

I think this is now the most popular re-plugin question. The plugin can only perform a partial update of properties. For example, if you must reassign the data below to your object

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

It will only update these properties. However, collections are handled differently, so if you need to redo them with the one below.

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

It will remove the mismatch item in your Controls collection instead of updating the corresponding item. The only way to get this to work is to scroll through the collection and display each individual item that you want to update. Your code is almost on the right track, I believe it should be

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

EDIT

You did not have a middle match argument when calling fromJS.

http://jsfiddle.net/3CtFq/

The mapping object is usually optional, but I assume that if you map the sub-object, as in your case, the plugin cannot say that the parent elements of this element were mapped without parameters.

Hope this helps.

+6
source

I thought that was the whole point of having a "key." I understand that there are reservations to this function, but I am surprised that there is no support.

In any case - this (unfortunately) is what I had to do to partially update the list of orders - this is a cycle and replacement of elements.

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

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

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

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

When I want to update one 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); } } 
0
source

Source: https://habr.com/ru/post/913550/


All Articles