Knockout.js ko.mapping.toJS not refreshing data in my view
-
25-10-2019 - |
Question
I fetch a json object from the server and populate my view. I then change the data, push it back to the server. I then fetch a new copy of the data hoping it will refresh my view with any changes. However that doesn't happen. TIA
$(document).ready(function() {
var customer_id = get_customer_id();
var data = load_model();
contract_model = ko.mapping.fromJS(data,{});
ko.applyBindings(contract_model);
}
function load_model(){
var url = '/ar/contract_json?contract_id='+get_contract_id();
var data = '';
$.ajax({
type:'GET',
url:url,
async:false,
success: function(returningValue){
data = returningValue;
}
});
return data;
}
This initial load works fine. I then do some stuff and change one of the observables and push that data back to server. Server gets the update and then I do a new fetch of the data so that view will refresh (i know i can pass back the new data in one step but this in code i haven't refactored yet).
function refresh_data(contract_model){
var url = '/ar/contract_json?contract_id='+get_contract_id();
$.post(url,function(data){
console.log(data);
ko.mapping.fromJS(contract_model,{},data);
ko.applyBindings(contract_model);
console.log(ko.mapping.toJS(contract_model))
});
}
function refresh_data(contract_model){
var url = '/ar/contract_json?contract_id='+get_contract_id();
$.post(url,function(data){
console.log(data);
ko.mapping.fromJS(contract_model,{},data);
console.log(ko.mapping.toJS(contract_model))
});
}
function push_model(contract_model,refresh){
var url = '/ar/update_contract';
var data = {'contract':ko.mapping.toJSON(contract_model)}
delete data['lines'];
$.post(url,data,function(return_value){
if (refresh){
refresh_data(contract_model);
};
});
}
The console messages all show the new data coming back but my view never updates.
Solution
I believe the problem is with the order of parameters you pass into the ko.mapping.fromJS
function when you are updating contract_model
.
You have:
ko.mapping.fromJS(contract_model,{},data);
you want:
ko.mapping.fromJS(data, {}, contract_model);
OTHER TIPS
@seth.miller's answer is correct. You can also leave out the middle "options" parameter if your contract_model
is the same one that was mapped earlier. If there are only two arguments, ko.mapping.fromJS
checks if the second argument has a "__ko_mapping__"
property. If so, it treats it as a target, otherwise it treats it as an options object.
Based upon @DBueno's observation - for anyone using typescript I strongly recommend commenting out this method signature from your knockout.mapping.d.ts
file.
// fromJS(jsObject: any, targetOrOptions: any): any;
Yes - just comment it out.
You'll then get a compile time error if you try to do :
ko.mapping.fromJS(item.data, item.target);
and you can replace it with the much safer
ko.mapping.fromJS(item.data, {}, item.target);
Safer because whether or not item.target
has been previously mapped (and therfore would have a __ko_mapping__
property) it will always copy the properties.