6

Using on our current project, we came to this point several times already.

How can I make sure some piece of Javascript code gets executed only after all bindings on the page have been applied by Knockout?

In my specific use case, I am using if-bindings to evaluate some configuration options and decide whether the elements inside should be rendered (= in the DOM) or not. Only after these if-bindings have been evaluated I need to count the number of DOM nodes inside a certain element. Obviously, if I count too early the if-bindings have not removed those unwanted DOM nodes yet, so the counting comes to a wrong result.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
connexo
  • 53,704
  • 14
  • 91
  • 128
  • May be afterRender callback (http://stackoverflow.com/questions/9306177/knockoutjs-bind-event-after-template-render) for root binding helps you? – TSV Aug 25 '15 at 14:22
  • If that was available on ko.applyBindings - yes. But it seems it's available only on certain specific bindings. I need a hook after all applicable bindings have been properly applied, including all components, sub-components etc. Like the state you have when no asynchronous calls are unfinished and the page is "complete" and "ready to go". – connexo Aug 25 '15 at 14:25
  • 2
    The only thing has came into my head - you can wrap the binding you want to process with a wrapping template binding - like in the http://jsfiddle.net/Le3bw37p/ fiddle - the function 'ar' will be called after the template binding content will has been rendered – TSV Aug 25 '15 at 15:10
  • You should probably include an actual repro of your situation. Without it the problem is trivially solved: you execute "*some piece of Javascript [after bindings have been applied]*" by executing that script after `applyBindings`... – Jeroen Aug 26 '15 at 12:41
  • @Jeroen That is hardly possible. Our project uses hundreds of viewmodels, fetches data from SAP and MS Dynamix, the C# creates a customer model in memory with hundreds of properties. The use case cannot meaningfully be reproduced in a fiddle. – connexo Aug 26 '15 at 15:36
  • How do you expect strangers to help you with a problem they can't reproduce? We can't *guess* at what would be representative of your situation and then *guess* at answers. – Jeroen Aug 26 '15 at 17:02
  • 1
    @Jeroen Rather than being a specific code problem, this is rather a problem every Knockout developer will run into sooner or later if they are applying Knockout on large scale projects. So see it as a generic question how a certain problem can be addressed using Knockout.js. – connexo Aug 27 '15 at 07:07

2 Answers2

7

ko.applyBindings() is a synchronous call, so the next statement should be only executed after it's done. If you have knockout components, they can either be loaded synchronously or asynchronously. So, for example

var vm = new ViewModel();
ko.applybindings(vm);
//
CountRenderedElements();

should give you correct result.

Community
  • 1
  • 1
Dandy
  • 2,177
  • 14
  • 16
4

May be this will be helpful. You can wrap your binding with template binding (and put any another binding(s) inside the template) and pass the 'afterRender' handler. This handler will be called after content will have been rendered. I've beautified the jsfiddle mentioned above (in the comment):

var model = {
  afterRenderCallback: function() {
    // this method will be called after content rendered
    var divContent = document.getElementById("textdiv").innerHTML;
    alert(divContent);
  },
  txt: ko.observable("this text will be substituted in the div")
};

ko.applyBindings(model);
.content {
    border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<script type="text/html" id="wrappingTemplate">
<div id="textdiv" class="content" data-bind="text: txt"></div>
</script>

<!-- ko template: { name: 'wrappingTemplate', afterRender: afterRenderCallback } -->
<!-- /ko-->
TSV
  • 7,538
  • 1
  • 29
  • 37