78404

Knockout nested components: $(document).ready() … runs before nest component is loaded

Question:

So I have several nested knockout components:

<component1> <component2> .... </component2> </component1>

component1 is my own component, written by me, but component2 is a third party code and I am not supposed to change it. Here is the problem:

In $(document).ready(), I use jquery $('button').click(...) to assign click even handler to, say, all buttons, for simplicity. The result it, only buttons inside component1 and outside component2 get the handler, and none of the button inside component2 has the handler (nothing happens when I click on them).

Note that component2 relies on (consumes) data from some ajax call, so this may delay its loading. Because ajax is async, so it's possible that $(document).ready() runs even before the ajax finishes, and therefore, the $('button').click(...) didn't catch the buttons inside components as they have not been rendered yet.

<strong>Additional problem:</strong> The viewmodel1 of component1 seems to always be empty when inside component2. The context is correct; it's just empty (for example: an array of the viewmodel1 is empty inside component2, not not empty when outside component2.

How to make all the buttons inside component2 get the handler?

Answer1:

If you can't modify component2 and it doesn't provide you with a way to know when it's done loading then your only option is to loop until it's done loading. You can tell when it's done loading by testing for some class/id/etc that you know is inside the component.

<component1> <component2> ... <div class="some-class-I-know-will-be-here"></div> </component2> </component1> var loop = window.setInterval(function () { if ($('.some-class-I-know-will-be-here')[0]) { componentHasLoaded(); window.clearInterval(loop); } }, 100);

Note that this is indeed a hack/workaround. You should probably be passing an "onClick" function <em>into</em> the component which would wire it up appropriately.

<strong>[Edit]</strong>

This workaround is overkill for simply attaching click events. The other answers cover how this can be accomplished using jQuery's event delegation. This workaround is only practical when you need to modify the component's template/viewModel after it's been created and you have <em>no way to modify the component any other way</em>.

Answer2:

You should use <a href="http://api.jquery.com/on/" rel="nofollow">event delegation</a>, so that the DOM elements don't have to be present at the time you set up the trigger.

$(document).on('click', 'button', ...);

Answer3:

Try to use the jQuery feature of adding events handlers to any future element inside. Check the syntax on the code below.

$(document).on("click", "div", function(){ console.log(this.id); });

Even if some div is not loaded yet, the click event will work on those elements.

Answer4:

If the jQuery delegate event doesn't fit your scenario and you are using Knockout >= 3.5.0, you have the childrenComplete binding parameter you can use on your component.

I use it like that:

<script> window.componentReady= false; function onAccordionReady() { window.componentReady = true; } </script> <div data-bind="component: {name: 'my-component', childrenComplete: onComponentReady"> </div>

And on javascript, similar to CrimsonChris's answer but perhaps more performant:

<script> $(document).ready(function documentReady() { //Notice the named function if (!window.componentLoaded) { setTimeout(documentReady, 1000); return; //Don't forget the return! } //... All the code I want to execute on document ready that //dependent on the components having finished rendering } </script>

Recommend

  • MS Access 2010: Adding transaction management into a form
  • Is it expensive to create the Thread object or to actually start the thread?
  • How do I prepend to a stream in Bash?
  • How to autopopulate a field in SugarCRM form
  • Elasticsearch script query involving root and nested values
  • Why use database factory in asp.net mvc?
  • Web.config system.webserver errors
  • How do I configure context broker accept post requests from my remote sensor?
  • Silverlight DependencyProperty.SetCurrentValue Equivalent
  • ViewController With Transparent Background Entering Current ViewController With Push Transition
  • Zurb Foundation _global.scss meta styles for js?
  • OOP Javascript - Is “get property” method necessary?
  • How do I access an unhandled exception in an MVC Error view?
  • Jackson Parser: ignore deserializing for type mismatch
  • Mysterious problem with floating point in LISP - time axis generation
  • How to know when stdin is empty if it contains EOF?
  • How to use remove-erase idiom for removing empty vectors in a vector?
  • Google Custom Search with transparent background
  • Repeat a vertical line on every page in Report Builder / SSRS
  • Why is an OPTIONS request sent to the server?
  • Master page gives error
  • C# - Is there a limit to the size of an httpWebRequest stream?
  • Is my CUDA kernel really runs on device or is being mistekenly executed by host in emulation?
  • Javascript Callbacks with Object constructor
  • Join two tables and save into third-sql
  • Perl system calls when running as another user using sudo
  • vba code to select only visible cells in specific column except heading
  • Redux, normalised entities and lodash merge
  • How to make Safari send if-modified-since header?
  • Timeout for blocking function call, i.e., how to stop waiting for user input after X seconds?
  • Function pointer “assignment from incompatible pointer type” only when using vararg ellipsis
  • Rearranging Cells in UITableView Bug & Saving Changes
  • 0x202A in filename: Why?
  • Benchmarking RAM performance - UWP and C#
  • Angular 2 constructor injection vs direct access
  • File not found error Google Drive API
  • How does Linux kernel interrupt the application?
  • IndexOutOfRangeException on multidimensional array despite using GetLength check
  • Reading document lines to the user (python)
  • To Get the radio button value in ruby on rails