I asked this question previously, but it didn't get much response. It was obvious that I wasn't very clear what I was trying to achieve, so I'll try again.
Here's the jsFiddle to play around with
I'm working on an app that will (at an interval) call a web method, which will read a database and build up a JSON string to return which is used to construct my view model. When my view model changes I need to mutate all selected observables with the new data.
Take this as an example underlying model.
var model = {
people: [
{ forename: "Test", surname: "Person", numbers: [1,2,3] },
{ forename: "Another", surname: "Test", numbers: [4,5,6] }
]
};
Simple enough, a table of people that when a row is clicked on a modal will pop up showing a list of numbers
the selected person has. While this modal is open, the data could be being updated behind the scenes so if [Test Person] is selected (and the modal pops up) and someone, somewhere adds [7,8,9]
to their numbers
then I need the modal to refresh showing these additions.
Here's a basic view model (using ko.mapping
for brevity):
var viewModel = {
people: ko.mapping.fromJS(model.people),
selectedPerson: ko.observable(null),
refresh: function () {
ko.utils.arrayForEach(model.people, function (person) {
var last = person.numbers[person.numbers.length - 1];
var newNumber = last + 1;
person.numbers.push(newNumber);
});
ko.mapping.fromJS(model.people, this.people);
console.log(ko.toJSON(this));
}
};
When a row is clicked on, the person is stored in selectedPerson
which makes my modal become visible in the UI (I'm using Zurb Foundation):
<div id="modal" class="reveal-modal" data-bind="if: selectedPerson">
This modal shows a table of numbers
with a button named Update Observable - this calls a function to emulate the changes to the underlying model (in this case I'm taking the last item in numbers
incrementing it by 1 and pushing it on). When this is done I then re-create my view model (using ko.mapping
for brevity) and expect everything on the UI to be updated to reflect the change - all fine apart from the modal.
Looking at the JSON of my view model after re-mapping I can see that the view model has been updated correctly, but the selectedPerson
hasn't. I assumed selectedPerson
was a reference, not a copy, but I obviously assumed wrong and we end up with this.
{
"people": [
{
"forename": "Test",
"surname": "Person",
"numbers": [1,2,3,4,5,6,7,8]
},
{
"forename": "Another",
"surname": "Test",
"numbers": [4,5,6,7,8,9,10,11]
}
],
"selectedPerson": {
"forename": "Test",
"surname": "Person",
"numbers": [1,2,3]
}
}
So I suppose my question is: how do I update selectedPerson
(or in fact any other selected observable there may be) when the underlying model changes?
Your problem is that the
ko.mapping.fromJS(model.people, this.people)
empties and recreates yourpeople
collection. But yourselectedPerson
still references one of the oldperson
object.You need to tell the mapping plugin how you want to identify your items so it will update them:
Demo JSFiddle.
Or as an alternative solution you can reset your
selectedPerson
to one of the the newly createdperson
object after the mapping:Demo JSFiddle.