Refreshing a selected observable when parent is updated

坚强是说给别人听的谎言 提交于 2020-01-05 09:21:06

问题


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?


回答1:


Your problem is that the ko.mapping.fromJS(model.people, this.people) empties and recreates your people collection. But your selectedPerson still references one of the old person object.

You need to tell the mapping plugin how you want to identify your items so it will update them:

ko.mapping.fromJS(model.people, 
                 { key: function(item) { return item.forename; } }, 
                 this.people); 

Demo JSFiddle.

Or as an alternative solution you can reset your selectedPerson to one of the the newly created person object after the mapping:

this.selectedPerson(ko.utils.arrayFirst(this.people(), function(item){
        return item.forename() == this.selectedPerson().forename();
    }, this));

Demo JSFiddle.



来源:https://stackoverflow.com/questions/20124317/refreshing-a-selected-observable-when-parent-is-updated

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!