1
votes

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."
1
you cannot apply applybindings multiple times that how ko works .but you can try using cleanNode and re-applying bindings on the main div should work fine . cheers - super cool
If cleanNode then I will have to repopulate the entire model though? - Shiloh
indeed yes it re-evaluates fiddle here jsfiddle.net/supercool/BrsmC/47 . there is a better approach(not always) like you can write the dynamic content in div on first applybindings and hide it and inside ajax success show it . cheers - super cool
Does your web service to which you're making your Ajax calls return JSON or HTML? The usual KO approach would be to have a self.optInList = ko.observableArray([OptIn, OptIn2]) and then the DOM will be updated via a <div id="content1" data-bind="foreach: optInList">. Then when your Ajax call fetches new optIn items, you push them into the list and the DOM updates, bindings and all. - sifriday
The Ajax calls that I am having trouble with return HTML. I am already doing what you're describing when I make an Ajax call to update a previously rendered piece of HTML - Shiloh

1 Answers

1
votes

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).