I have the following model:
var model = {
A: 'One',
B: 'Two',
C: 'Three'
};
I bind various UI elements to these fields, which works great. However, I convert the model back to a JavaScript object so I can save any changes to the server:
var goingToServer = ko.toJS(model);
goingToServer
will include properties A, B and C. However, let's say property C is a huge chunk of data that will never change. I'd like to avoid sending this back to the server.
Is there a way to make toJS()
only include a predefined set of fields when converting a model back to a JavaScript object?
One thing I've been investigating is the Knockout Mapping plugin. It has a setting called include which is documented as such:
When converting your view model back to a JS object, by default the mapping plugin will only include properties that were part of your original view model, except it will also include the Knockout-generated _destroy property even if it was not part of your original object. However, you can choose to customize this array:
However, it appears this plugin doesn't work as documented, as ko.mapping.toJS()
will still include A, B and C, even if I pass in an include
array of ['A', 'B']
. I'm guessing this feature is intended to include additional fields that were not in the original model.
Is there a way to exclude certain properties when converting a model back to a JavaScript object, short of doing something hacky such as generating the object and manually removing the properties I don't want before sending to the server?
Have you tried the ignore
keyword? It has a similar usage to the include:
var mapping = {
'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
var viewModel = ko.mapping.toJS(data, mapping);
You could just use ignore when you do the original mapping of the server data, then it won't be on your object at all when you convert it back to JS.
If we have a complex object model instance under vm.Payment observable reference as:
{
"Summary": {
"Count": 12,
"PaymentAmount": 1500,
"PaymentMethod": "Cheque",
"PaymentDate": "2015-04-08T22:38:48.552Z",
"AgentOid": 1208795,
"AgentName": "Asere Ware"
}
//[...]
}
and need to ignore only some inner properties, then:
var mapping = {
'ignore': ['Summary.PaymentMethod', 'Summary.PaymentDate'] //avoid lost some initialized values.
};
// map updating existing observable (Payment) under my ViewMode (vm) with source "data" JSON object.
ko.mapping.fromJS(data, mapping, vm.Payment);
and this is the result for vm.Payment content, where only PaymentMethod and PaymentDate are keept:
{
"Summary": {
"Count": 0,
"PaymentAmount": 0,
"PaymentMethod": "Cheque",
"PaymentDate": "2015-04-08T22:38:48.552Z",
"AgentOid": undefined,
"AgentName": undefined
}
//[...]
}
Ok, I've figured out one solution that works though I'm hoping there's a better approach. The trick is to ignore these fields in the mapping plugin, then add them in manually as computed fields. Computed fields will never end up in the generated JavaScript object when toJS
is called.
// Utility function to quickly generate a computed field
ko.readonly = function (value)
{
return ko.computed(function ()
{
return value;
});
};
// View Model
var ViewModel = function (project)
{
var readonly = ['B', 'C', 'D']; // Fields I want to ignore
var mapping = {
'ignore': readonly //Read-only properties, we'll add these manually as computed fields
};
// Use Mapping plugin to make everything observable, except the fields above
ko.mapping.fromJS(project, mapping, this);
// Now loop through the read only array and add these fields in as computed
for(var i = 0; i < readonly.length; i++)
{
var field = readonly[i];
this[field] = ko.readonly(project[field]);
}
}
I can now bind to these view model as normal:
ko.applyBindings(new ViewModel(data));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With