1

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);
jbabey
  • 45,965
  • 12
  • 71
  • 94
  • Why are you using `cleanNode`? You probably meant to use the `if` or `with` bindings. http://stackoverflow.com/questions/15063794/can-cleannode-be-used-to-clean-binding – CrimsonChris Aug 17 '15 at 16:06

2 Answers2

4

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

haim770
  • 48,394
  • 7
  • 105
  • 133
1

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/

CrimsonChris
  • 4,651
  • 2
  • 19
  • 30