Shiloh
Shiloh

Reputation: 1878

KnockoutJS Binding

I have view model that I created in an ajax application. The ko.applyBindings() gets called when I render my first piece of html. Later I make a second ajax call to render another section of html and bind to the same view model. However, because the html elements from the 2nd ajax were not present when I first called ko.applyBindings() then when my second section of html is retrieved, data-bind does not work.

Here's a very simple example where the first check box binds correctly. Then content2 would be loaded via a subsequent ajax call and the checkbox fails to bind

<div id='container'>
    <div id="content1"> <span>Opt In</span>
        <input data-val-required="field is required." id="cbOptIn" name="OptIn" type="checkbox" data-bind="checked: OptIn"/>
    </div>    
</div



var ViewModel = function () {
    var self = this;
    self.OptIn = ko.observable(false);
    self.OptIn2 = ko.observable(false);
}

var appVM = new ViewModel();
ko.applyBindings(appVM);

appVM.OptIn(true);

var content2 = document.createElement('div');
content2.id = 'content2';
content2.innerHTML = '<span>Opt In2</span><input data-val-required="field is required." id="cbOptIn2" name="OptIn2" type="checkbox" data-bind="checked: OptIn2"/>';
document.getElementById('container').appendChild(content2);

appVM.OptIn2(true);

I have also tried to solve this by creating two separate view models but then I get

"You cannot apply bindings multiple times to the same element."

Upvotes: 1

Views: 390

Answers (1)

dfperry
dfperry

Reputation: 2258

I'm assuming your stripped down version is not functionally the same as your actual case and you're using a jQuery load() call to bring the fragment in. If that's the case, there are a couple of functions you can use to re-apply the bindings: ko.dataFor(), ko.cleanNode(), and ko.applyBindings().

If you're loading a fragment into an element:

element.load('path/to/fragment.html', function() {
  // do stuff
});

and that element or some other parent element has been bound to the view model, you can re-apply the bindings with a function like the following:

var reapplyBindings = function(element) {
  // grab the viewmodel that has been applied to this element
  var viewModel = ko.dataFor(element);

  // if the viewmodel exists, clean the node and re-apply the viewmodel
  // to the element (and all of it's children)
  if(viewModel) {
    ko.cleanNode(element);
    ko.applyBindings(vm, element);
  }
}

Now, to use this function:

element.load('path/to/fragment.html', function() {
  reapplyBindings(element);
  // do stuff you were doing before
});

This works because even though you may not have applied the viewmodel explicitly to the element being loaded into, knockout can still grab the viewmodel from further up the DOM.

The only real caveat with this approach is the cleanNode function; it will remove any event handlers, so they would need to be re-applied (unless you're attaching them to a parent element, in which case you're good).

Upvotes: 1

Related Questions