I am trying to create knockoutjs bindings for jquery ui dialogs, and cannot get the dialog to open. The dialog element is created correctly, but seems to have display: none
that calling dialog('open')
doesn't remove. Also, the call to dialog('isOpen')
returns the dialog object rather than a boolean.
I am using the latest knockoutjs and jquery 1.4.4 with jquery ui 1.8.7. I've also tried it with jQuery 1.7.1 with the same results. Here's my HTML:
<h1 class="header" data-bind="text: label"></h1>
<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Dialog test'}">foo dialog</div>
<div>
<button id="openbutton" data-bind="dialogcmd: {id: 'dialog'}" >Open</button>
<button id="openbutton" data-bind="dialogcmd: {id: 'dialog', cmd: 'close'}" >Close</button>
</div>
and this is the javascript:
var jQueryWidget = function(element, valueAccessor, name, constructor) {
var options = ko.utils.unwrapObservable(valueAccessor());
var $element = $(element);
var $widget = $element.data(name) || constructor($element, options);
$element.data(name, $widget);
};
ko.bindingHandlers.dialog = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
jQueryWidget(element, valueAccessor, 'dialog', function($element, options) {
console.log("Creating dialog on " + $element);
return $element.dialog(options);
});
}
};
ko.bindingHandlers.dialogcmd = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
$(element).button().click(function() {
var options = ko.utils.unwrapObservable(valueAccessor());
var $dialog = $('#' + options.id).data('dialog');
var isOpen = $dialog.dialog('isOpen');
console.log("Before command dialog is open: " + isOpen);
$dialog.dialog(options.cmd || 'open');
return false;
});
}
};
var viewModel = {
label: ko.observable('dialog test')
};
ko.applyBindings(viewModel);
I have set up a JSFiddle that reproduces the problem.
I am wondering if this has something to do with knockoutjs and event handling. I tried returning true
from the click handler, but that did not appear to affect anything.
It looks like writing to the widget to .data("dialog") and then trying to operate on it is causing an issue. Here is a sample where
.data
is not used and the open/close is called based on the element: http://jsfiddle.net/rniemeyer/durKS/Alternatively, I like to work with the dialog in a slightly different way. I like to control whether the dialog is open or closed by using an observable. So, you would use a single binding on the dialog itself. The
init
would initialize the dialog, while theupdate
would check an observable to see if it should call open or close. Now, the open/close buttons just need to toggle a boolean observable rather than worry about ids or locating the actual dialog.Used like:
Here is a sample: http://jsfiddle.net/rniemeyer/SnPdE/
I made a little change to RP Niemeyer's answer to allow the dialog's options to be observables
http://jsfiddle.net/YmQTW/1/
Get the observables values with ko.toJS to initialize the widget
and check for observables on update
Adding this here because this is what most people find when searching on issues with jQuery UI Dialog and Knockout JS.
Just another option to avoid the "double binding" issue explained in the above answer. For me, the setTimeout() was causing other bindings to fail that require the dialog to be initialized already. The simple solution that worked for me was making the following changes to the accepted answer:
Add class='dialog' to any elements using the custom dialog binding.
Call this after page load, but before calling ko.applyBindings():
$('.dialog').dialog({autoOpen: false});
Remove the
setTimeout
inside theinit
of the custom binding, and just call the code directly.Step 2 makes sure that any jQuery UI Dialogs have been initialized prior to any KO bindings. This way jQuery UI has already moved the DOM elements, so that you don't have to worry about them moving in the middle of applyBindings. The init code still works as-is (other than removing setTimeout) because the dialog() function will just update an existing dialog if already initialized.
An example of why I needed this is due to a custom binding I use to update the title of the dialog:
I use a separate binding for this instead of the update function for the main dialog binding, because I only want to update the title, not other properties such as height and width (don't want the dialog to resize just because I change the title). I suppose I could also use update and just remove height/width, but now I can do both/either and not worry about the setTimeout being completed or not.
There's now this library that has all the JQueryUI bindings for KnockoutJS including, of course, the dialog widget.
This is a variation of the great RP Niemeyer binding handler, which is useful for a differente scenario.
To allow the edition of an entity, you can create a
<div>
with edition controls, and use awith
binding, which depends on an observable made on purpose for the edition.For example, to allow the edition of a
person
, you can create and observable likeeditedPerson
, and create a div with edition controls, with a binding like this:When you add a person to the observable lke so:
the binding makes the
div
visible. When you finish the edition, you can set the observable to null, like soand the
div
will close.My variation of RP Niemeyer's bindingHandler allows to automatically show this div inside a jQuery UI dialog. To use it you simply have to keep the original
with
binding, and specify the jQuery UI dialog options like so:You can get the code of my binding handler, and see it in action here:
http://jsfiddle.net/jbustos/dBLeg/
You can modify this code easily to have different defaults for the dialog, and even to make these defaults configurable by enclosing the handler in a js module, and adding a public configuration function to modify it. (You can add this function to the binding handler, and it will keep working).