Reputation: 33
Though this might look like many of the similar questions about knockout mapping plugin and partial view model updation, I did not see the issue im facing here reported in all my searching.
I will detail with an example below but in short this is what I am trying to do.
First I create View Model from a Javascript Object using KO Mapping plugin's ko.mapping.fromJS
.
Now I want to update only a part of the view model with the data that I receive from the server. So.
ko.mapping.toJS
ko.mapping.fromJS
The problems
Here is a simplified example.
The View
<div>
<label data-bind="text: mainTitle"></label>
<div data-bind="with: layer0">
<label>Name - </label>
<label data-bind="text: name"></label>
<label>Type - </label>
<label data-bind="text: type"></label>
</div>
</div>
Data model which will be used to make the ViewModel
var dataModel = {
mainTitle: "Main Title",
layer0:{
itemDetails:{
name:"item1name",
type:"type1"
},
otherDetails:{
prop1: "prop1Value",
prop2: "prop2Value"
}
}
This JS object dataModel
is converted to KO View Model by
var viewModel = ko.mapping.fromJS(dataModel);
and applied to the view above by applyBindings and it displays data as expected
Now I receive this JS object from the server
var newJSobjectFromServer = {
name: "item2Name",
description: "item2Description" // new property that was not on first model
type: "type2"
}
Now I cannot do
viewModel.layer0.itemDetails(newJSobjectFromServer);
because in the viewModel
which was created by the mapping plugin itemDetails
is a property and not a method only name
and type
are methods
So I first extract a JS object from the viewModel by
var viewModelJS = ko.mapping.toJS(viewModel);
Then I update a part of the viewModelJS
with new server data
viewModelJS.layer0.itemDetails = newJSobjectFromServer;
so viewModelJS
becomes
{
mainTitle: "Main Title",
layer0:{
itemDetails:{
name: "item2Name",
description: "item2Description" // new property
type: "type2"
},
otherDetails:{
prop1: "prop1Value",
prop2: "prop2Value"
}
}
}
Then I try to update the viewModel by
ko.mapping.fromJS(viewModelJS , viewModel);
also tried
ko.mapping.fromJS(viewModelJS , {}, viewModel);
but the result is
layer0.itemDetails.name()
is still "item1"
and layer0.itemDetails.type()
is still "type1"
while the new observable layer0.itemDetails.description()
is present and has the value "item2Description"
now if I repeat the updating with another server returned object
{
name: "item3Name",
description: "item3Desctiption"
type: "type3"
}
this time all the values in viewModel
remain same as last time that is
layer0.itemDetails.name()
is still "item1"
and layer0.itemDetails.type()
is still "type1"
and
layer0.itemDetails.description()
is still "item2Description"
and thus nothing changes in the view.
The complexities of the objects handled make it necessary for me to use the mapping plugin only the example is simplified but does completely portray the problem I am facing. I am really reaching out for someone to guide me how to get that part of the viewModel updated with the data from the server in the above exact scenario. If that is solved I can apply it to the more complex real world scenario that I am facing. Thanks in advance.
Upvotes: 0
Views: 1012
Reputation: 1186
Your UI is still bound to the same ViewModel and running the mapping again doesn't refresh it.
You can use a different approach where you create a parent view model and set your "viewModel" as an observable member of the parent ViewModel. Check out this fiddle for a very rushed (but working) example of this behavior
http://jsfiddle.net/barryman9000/4SKNd/2/
var parentViewModel = function (data) {
var _self = this;
_self.Data = ko.observable('');
_self.LoadData = function () {
_self.Data(new viewModel(data));
};
_self.GetNewData = function (data, item) {
_self.Data(new viewModel(newJSobjectFromServer));
};
_self.LoadData();
};
var viewModel = function (data) {
var _self = this;
ko.mapping.fromJS(data, {}, _self);
_self.Data = ko.observable(data);
};
ko.applyBindings(new parentViewModel(dataModel));
Also, if the GetNewData() function makes an ajax call to get the data, this will still refresh your UI - that's how I use this "pattern."
UPDATE
Some ideas: 1) restructure your data so it's uniform in all cases. Otherwise you'll be building each of the viewModels very differently which is just going to cause readability headaches later. http://jsfiddle.net/barryman9000/Q5RRr/1/
2) If you can't restructure the data, ditch the mapping plugin and do everything manually. I'd say this yields much cleaner markup and gives you more control of the data. http://jsfiddle.net/barryman9000/5DD27/
Upvotes: 1