jbabey
jbabey

Reputation: 46657

Duplicate elements when cleaning and re-applying bindings with child viewModels

I have a viewModel containing child viewModels in an observableArray that is bound to some markup using a foreach binding.

Later in the page life cycle, I need to remove the old viewModel and apply a new one in its place. I do this by calling ko.cleanNode() and then calling applyBindings with the new view model.

For some reason when this happens, all of the child view models end up getting duplicated markup even though the parent observableArray has the correct number of viewModels in it.

I am sure I am just using some knockout functionality incorrectly, but I cannot figure out how to get it working.

Issue is replicated here: http://jsfiddle.net/a7xLxwxh/

Markup:

<div class="container">
    <label>RANGES</label>
    <div class="rangeContainer" data-bind="foreach: ranges">
        <div class="range"> 
            <span>START <br /><span data-bind="text: start"></span></span>
            <span>END <br /><span data-bind="text: end"></span></span>
        </div>
    </div>
</div>

JS:

var ParentViewModel = function (data) {
    var self = this;

    self.ranges = ko.observableArray([]);

    data.Ranges.forEach(function (range) {
        self.ranges.push(new RangeViewModel(range));
    });
};

var RangeViewModel = function (data) {
    var self = this;

    self.start = ko.observable(moment(data.Start).format('MM/DD/YYYY'));
    self.end = ko.observable(moment(data.End).format('MM/DD/YYYY'));
};

var vm = new ParentViewModel({
    Ranges: [{
        Start: '/Date(1439438400000)/',
        End: '/Date(1439611200000)/'
    },
    {
        Start: '/Date(1439265600000)/',
        End: '/Date(1439352000000)/'
    }]
});

var element = $('.container')[0];
ko.applyBindings(vm, element);

ko.cleanNode(element);
ko.applyBindings(vm, element);

Upvotes: 1

Views: 291

Answers (2)

CrimsonChris
CrimsonChris

Reputation: 4651

Use the with binding in order to swap out view models. cleanNode is an undocumented method.

<div class="container" data-bind="with: viewModel">
    ...
</div>

http://jsfiddle.net/a7xLxwxh/3/

Upvotes: 1

haim770
haim770

Reputation: 49125

Later in the page life cycle, I need to remove the old viewModel and apply a new one in its place.

The better way to replace the view-model is to make the view-model itself an observable:

var vm = ko.observable(new ParentViewModel(
{
    Ranges: [{
        Start: '/Date(1439438400000)/',
        End: '/Date(1439611200000)/'
    },
    {
        Start: '/Date(1439265600000)/',
        End: '/Date(1439352000000)/'
    }]
}));

ko.applyBindings(vm);

Then when you want to replace it:

vm(new ParentViewModel({
    Ranges: [{
        Start: '/Date(1439438400000)/',
        End: '/Date(1435611200000)/'
    }]
}));

See Fiddle

Upvotes: 4

Related Questions