Search code examples
jqueryjquery-uiknockout.jsjquery-ui-dialog

Knockout component using jQuery-ui dialog ignores bindings


I'm using knockout components to modularize my UI. One of them instances a jQuery-ui dialog containing few buttons. All data-bind on them, and on anything contained in the dialog, is simply ignored. Yes, I have read the warnings here against mixing knockout and jQuery, but which alternatives have I? Well, in this simple case I have a workaround. Keep reading.

My component is split into test.html

<div id="trashcan-dlg">
    <button data-bind="click: trashcanRecoverAll">Recover all</button>
</div>

and test.js

"use strict";

define(["jquery", "jquery-ui"], function($) {

    function TrashcanWidgetViewModel(params) {

        $("#trashcan-dlg")
            .dialog({
                resizable: false,
                height: "auto",
                width: "auto",
                modal: false,
                title: "Trashcan management"
            });
    }

    TrashcanWidgetViewModel.prototype.trashcanRecoverAll = function() {
        console.log("trashcanRecoverAll");
    };

    return TrashcanWidgetViewModel;
});

registered with:

ko.components.register("test", {
    viewModel: {require: "widgets/test"},
    template:  {require: "text!widgets/test.html"}
});

The problem does not change if I define a custom binding for the dialog. No error, nothing. If jQuery-ui dialog rewrites the DOM, this makes sense: there are no data-bind directives in the resulting DOM. Or I'm missing something obvious?

In this simple case the workaround is to get rid of the click binding and use non-ko click event handling. Just add the following to the constructor:

this.toolbarTrashcan = function() {
    console.log("trashcanPurgeSelected");
};
$("button", "#trashcan-dlg").on("click", this.toolbarTrashcan);

Thanks for your time!


Solution

  • I believe that your problem is that you make this call $("#trashcan-dlg").dialog(...) before the component instantiating is complete. Your click binding should start woring if you change that part of code to:

    setTimeout(function () {
        $("#trashcan-dlg")
            .dialog({
                resizable: false,
                height: "auto",
                width: "auto",
                modal: false,
                title: "Trashcan management"
            });
    }, 0);
    

    setTimeout(...) would put $("#trashcan-dlg").dialog(...) call to the end of JS event loop so it would be executed when ko infrastructure has completed with DOM manipulations.