[knockout.js] KnockoutJs v2.3.0 : Error You cannot apply bindings multiple times to the same element

I've just upgraded to 2.3.0 and now I'm getting the error

You cannot apply bindings multiple times to the same element.

that I wasn't getting in 2.2.1.

I'm getting a partial view from my MVC controller and adding it to the page after clicking on an href. The error happens the second time I click on the link to get the partial view. I'm doing this multiple times.

Is there a way to clear this out and avoid the new error thrown?

Here's my code:

$.get(url + "GetAssignedCompaniesView?layoutId=" + layoutId + "&noCache=" + new Date().getMilliseconds(), function (result) {
              $("#editAssignedPartial").html($(result));
              showEditAssignedArea(true);
              $(window.document).ready(function () {
                 // error is thrown here
                 ko.applyBindings(self, window.document.getElementById("editAssigned"));
                 $("#layoutId").attr("value", layoutId);
                 updateTypeHiddenElement.attr("value", "companies");
      });
    });
<div id="editAssignedPartial">
</div>

$(document).ready(function () {
  'use strict';
  var vm = new Vm();
  ko.applyBindings(vm, document.getElementById("area1"));
});

This question is related to knockout.js

The answer is


If you are reusing an element over and over (A bootstrap modal dialog in my case), then calling ko.applyBindings(el) multiple times will cause this problem.

Instead just do it once like this:

if (!applied) {
    ko.applyBindings(el);
    applied = true;
}

Or like this:

var apply = function (viewModel, containerElement) {
    ko.applyBindings(viewModel, containerElement);
    apply = function() {}; // only allow this function to be called once.
}

PS: This might happen more often to you if you use the mapping plugin and convert your JSON data to observables.


There are a lot of great answers for this issue but I have a noobie answer.

I found that I accidentally added the same Script in two places and it was trying to bind twice. So before you pull your hair out on a simple mistake, make sure you check for this issue.


i had the same problem and I solved it.

var vm = new MessagesViewModel()
ko.applyBindings(vm)

function ShowMessagesList() {
   vm.getData("MyParams")
}

setInterval(ShowMessagesList, 10000)

ko.cleanNode($("#modalPartialView")[0]);
ko.applyBindings(vm, $("#modalPartialView")[0]);

works for me, but as others note, the cleanNode is internal ko function, so there is probably a better way.


Updated Answer

Now that we can use dataFor() to check if the binding has been applied, I would prefer check the data binding, rather than cleanNode() and applyBindings().

Like this:

var koNode = document.getElementById('formEdit');
var hasDataBinding = !!ko.dataFor(koNode);
console.log('has data binding', hasDataBinding);
if (!hasDataBinding) { ko.applyBindings(vm, koNode);}

Original Answer.

A lot of answers already!

First, let's say it is fairly common that we need to do the binding multiple times in a page. Say, I have a form inside the Bootstrap modal, which will be loaded again and again. Many of the form input have two-way binding.

I usually take the easy route: clearing the binding every time before the the binding.

var koNode = document.getElementById('formEdit');
ko.cleanNode(koNode);
ko.applyBindings(vm, koNode);

Just make sure here koNode is required, for, ko.cleanNode() requires a node element, even though we can omit it in ko.applyBinding(vm).


In my case, I was adding to a non existing element, or, I was adding bindings to an element that maybe exists, but it's parent did not. Similar to this:

var segDiv =  $("#segments"); //did not exist, wrong id
var theDiv = segDiv.html("<div></div>");

ko.applyBindings(someVM, theDiv);

As far as I can tell, this error seems a bit overloaded in the sense that it will fire on a lot of different errors that can happen with the element, like it not existing. As such, the error description can be highly deceptive. It should have probably read:

"Failure to bind bindings to element. Possible reasons include: multiple binding attempts, element not existing, element not in DOM hierarchy, quirks in browsers, etc"


Two things are important for above solutions to work:

  1. When applying bindings, you need to specify scope (element) !!

  2. When clearing bindings, you must specify exactly same element used for scope.

Code is below

Markup

<div id="elt1" data-bind="with: data">
    <input type="text" data-bind="value: text1" >
</form>

Binding view

var myViewModel = {
  "data" : {
    "text1" : "bla bla"
  }
}:

Javascript

ko.applyBindings(myViewModel, document.getElementById('elt1'));

Clear bindings

ko.cleanNode(document.getElementById('elt1'));

You should never apply bindings more than once to a view. In 2.2, the behaviour was undefined, but still unsupported. In 2.3, it now correctly shows an error. When using knockout, the goal is to apply bindings once to your view(s) on the page, then use changes to observables on your viewmodel to change the appearance and behaviour of your view(s) on your page.


I was getting the same error in IE7/IE8. Worked fine in all other browsers including IE9/IE10.

What I found worked for me was eliminating self closing tags.

Bad

<div>
    <span data-bind="text: name"/>
</div>

Good

<div>
    <span data-bind="text: name"></span>
</div>

I had this error occur for a different reason.

I created a template for save/cancel buttons that I wanted to appear at top and bottom of the page. It worked at first when I had my template defined inside of a <script type="text/html"> element.... but then I heard you could optionally create a template out of an ordinary DIV element instead.

(This worked better for me since I was using ASP.NET MVC and none of my @variableName Razor syntax was being executed at runtime from inside of the script element. So by changing to a DIV instead, I could still have the MVC Razor engine generate HTML inside my KnockoutJs template when the page loads.)

After I changed my template to use a DIV instead of a SCRIPT element, my code looked like this.... which worked fine on IE10. However, later when I tested it on IE8, it threw that....

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

HTML

<div id="mainKnockoutDiv" class="measurementsDivContents hourlyMeasurements">

  <div id="saveButtons_template" style="display: none;">
    ... my template content here ...
  </div>

  <!--ko template: { name: 'saveButtons_template' } -->
  <!--/ko-->

  Some_HTML_content_here....

  <!--ko template: { name: 'saveButtons_template' } -->
  <!--/ko-->

</div>

JavaScript

ko.applyBindings(viewModel, document.getElementById('mainKnockoutDiv'));

THE SOLUTION:

All I had to do was move my saveButtons_template DIV down to the bottom, so that it was outside of the mainKnockoutDiv. This solved the problem for me.

I suppose KnockoutJs was trying to bind my template DIV multiple times since it was located inside the applyBindings target area... and was not using the SCRIPT element.... and was being referenced as a template.


Something that can happen as well which throws this exception is the following. Say you have:

ko.applyBindings(myViewModel1, document.getElementById('element1'));
...
ko.applyBindings(myViewModel2, document.getElementById('element2'));

Now, when both #element1 and #element2 don't exist you'll get the error. The reason is that Knockout's applyBindings falls back on document.body as root element when #element1 and #element2 are not found. Now it tries to apply binding twice on the body...

Not a nice fallback of Knockout if you ask me. I'd rather have a clear error message that the element does not exist in the DOM (yet).

Hope this helps some people.


I finally solved mine by returning { controlsDescendantBindings: true } in the init function of the binding handler. See this