I want to add animation when working with observable not observableArray which is already available in knockout documents.
Consider a <div data-bind="text: fullname"/> which fullname is an observable. I want the content of <div> element animate and slides down when the value of fullname changes. Something like:
<div data-bind="text: fullname, transition: slideDown"/>
Also, I'm using Durandal if it helps.
Question: How can I assign transition animations to observables?
The beforeRemove and afterAdd are just wrappers around the subscribe function.
ko.bindingHandlers['transition'] = {
init: function(element, valueAccessor, allBindings) {
var transition = valueAccessor(),
target = allBindings.get('text'),
subscriptions = [
target.subscribe(target, onChange),
target.subscribe(target, onBeforeChange, 'beforeChange')
];
function onChange() {
// handle transition when target updates
}
function onBeforeChange() {
// handle transition BEFORE target updates
}
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
subscription[0].dispose();
subscription[1].dispose();
});
}
};
You can create a custom binding handler which does the animation for you:
ko.bindingHandlers.textSlide = {
init: function(element, valueAccessor) {
$(element).text(ko.unwrap(valueAccessor()));
},
update: function(element, valueAccessor) {
var value = ko.unwrap(valueAccessor());
if (value != $(element).text()) {
$(element).slideUp(function() {
$(this).text(value).slideDown();
});
}
}
};
See http://jsfiddle.net/ro0u9Lmb/
Related
So I am using GSAP TweenLite and Draggable, as well as KnockoutJS to create nested resizeable observable elements (A div with an image inside). My goal is to resize and reposition those elements by number inputs and dragging and resizing, like a layout generator for Java/Android does. My code currently allows me to monitor changes caused by drag and resize actions, however it does not react to updates on the Knockout bindings or observables.
Javascript:
function object(id) {
var self = this;
self.id = ko.observable(id);
self.parent = ko.observable();
self.childNodes = ko.observableArray([]);
self.x = ko.observable(100);
self.y = ko.observable(50);
self.w = ko.observable(100);
self.h = ko.observable(100);
}
function EditorViewModel() {
this.self = this;
self.Objects = ko.observableArray([new object("1")]);
self.Objects().push(new object("2"));
self.Objects()[0].childNodes.push(new object("1.1"));
self.Objects()[0].childNodes()[0].childNodes.push(new object("1.1.1"));
self.chosenObject = ko.observable();
ko.bindingHandlers.dragResize = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var drag = element;
if (element.parentNode.parentNode == $(document.body)) {
parent = element.parentNode;
} else {
var parent = element.parentNode.parentNode;
}
var handle = $("<div class='resize-handle'></div>").appendTo(drag);
TweenLite.set(handle, {bottom: 0, right: 0});
Draggable.create(drag,
{
type: 'top left', bounds: parent,
onDragStart: function ()
{
if (typeof Draggable.get(parent) !== 'undefined') {
untilRange(element, false); //sets all parent Dragagbles to disabled to prevent quirks when dragging child
}
},
onDragEnd: function ()
{
if (typeof Draggable.get(parent) !== 'undefined') {
untilRange(element, true); //same as above, but re-enables them after drag
}
},
onDrag:function (e){
val.x(this.x);
val.y(this.y);
}
});
Draggable.create(handle, {
type: "top,left",
onPress: function (e) {
e.stopPropagation(); // cancel drag
},
onDrag: function (e) {
val.w(this.x);
val.h(this.y);
TweenLite.set(drag,{width:val.w(),height:val.h()});
},
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var drag = Draggable.get(element);
element.x=val.x();
drag.update();
}
}
HTML:
<div id="range" data-bind="template:{name: 'elementTemplate',foreach:Objects()}">
</div>
<!--Templates-->
<script type="text/html" id="elementTemplate">
<!--<pre style="position:relative;width: 100px" data-bind="text: ko.toJSON($data, null, 2)"></pre>-->
<div class="draggable" data-bind="attr:{id:id}, dragResize:{x:x,y:y,h:h,w:w}">
<div data-bind="template:{name: 'elementTemplate',foreach:childNodes()}">
</div>
<img src="res/Download.jpg" class="mapClass" alt="If you see this we haven't done well"/>
</div>
</script>
If someone has a different suggestion on how to reach my goal, please let me know, the only part I would like to keep is KnockoutJS, because my teacher recommended it.
Your code looks not too bad. I was not able to run it unmodified - I missed for example the untilRange function. So I stripped it down to the bare essentials to test how to have the observable update the position and vice versa. You will have to reapply to your original code.
The problem you maybe facing is one you run across alot of times, when you try to do a new binding in knockout, that updates observables in two directions:
- You drag your object which modifies your observable
- You modify your observable, which modifies your object
There are situations where you need to take care, that the events, that modify your observable do not trigger the update of your binding. But this is speculation, as I was not able to run the unmodified code.
Still from making a smaller example from your code, two way update seems to work fine, even without taking cycles into account. I found only that inside the update() function of the binding I would not call the Draggable.update() at all. Apart from that the two way binding should work.
See this fiddle:
https://jsfiddle.net/domoran/1dk1hc4v/5/
Javascript:
function object(id) {
var self = this;
self.id = ko.observable(id);
self.x = ko.observable(0);
self.y = ko.observable(0);
}
ko.bindingHandlers.dragResize = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var lastSelected = val.last;
Draggable.create(element, {
type: "x,y",
onDrag: function(e) {
lastSelected(ko.dataFor(element));
val.x(this.x);
val.y(this.y);
}
});
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var updateCoords = function() {
// since this is invoked inside a computed, it will be invoked,
// whenever the observables x and y change
TweenLite.set(element,{x:val.x(),y:val.y()});
};
var val = ko.unwrap(valueAccessor());
// make the updateCoors function call whenever one of its observables
// changes
updateCoords();
}
};
function EditorViewModel() {
var self = this;
self.Objects = ko.observableArray([new object("1"), new object("2")]);
self.lastSelected = ko.observable();
}
ko.applyBindings(new EditorViewModel());
HTML:
<body>
<div id="range" data-bind="template:{name: 'elementTemplate',foreach:Objects }">
</div>
<!--Templates-->
<script type="text/html" id="elementTemplate">
<div data-bind="attr:{id:id}, dragResize:{x:x,y:y,last:$root.lastSelected}">
<img width=80 src="https://upload.wikimedia.org/wikipedia/commons/4/4c/World_borders_lamb_azi.png" class="mapClass" alt="If you see this we haven't done well" /><span data-bind="text: x() + '/' + y()"></span>
</div>
</script>
<div data-bind="with:lastSelected">
<label>x</label><input type="text" data-bind="value: x" />
<label>y</label><input type="text" data-bind="value: y" />
</div>
</body>
I'm making my first custom binding. I would like to be able to specify what text that appears on an element based on a resource file. Something like this:
var exampleResource = {
hello: 'world'
};
ko.bindingHandlers.resource = {
init: function (element, valueAccessor) {
var value = valueAccessor();
ko.bindingHandlers.text.update(element, function() {
return exampleResource[value] || '';
});
}
};
<span data-bind="resource: 'hello'"></span>
Should I use ko.bindingHandlers.text as above?
Since the resource variable isn't observable, is there any point of adding the update callback for the binding? If I understand it correctly it will only get called if an observable is passed as the value?
You'd need an update if you want to support the input for your binding handler to be dynamic. In your example you don't do that, but you could. Here's an example:
var exampleResource = {
hello: 'world',
goodbye: 'drowl'
};
ko.bindingHandlers.resource = {
update: function (element, valueAccessor) {
var key = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.text.update(element, function() {
return exampleResource[key] || key;
});
}
};
ko.applyBindings({ myObs: ko.observable('goodbye') });
span { font-weight: bold; color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
Static: <span data-bind="resource: 'hello'"></span>
<hr>
Dynamic: <span data-bind="resource: myObs"></span>
- based on: <select data-bind="value: myObs, options: ['hello', 'goodbye']"></select>
If you don't need this dynamicness you could stick to your old solution. However, in that case I'd question the added value of KnockoutJS for resources in general :-)
I have two identical Binding Handlers and one of them is not firing, because it has no ko.unwrap(valueAccessor()); in the "update:" part of it.
Best way to see what is going on, is to look at my jsfiddle. By hitting the button the first span toggles the background, but the second not.
When you uncomment line 26 "ko.unwrap(valueAccessor());" the 2nd update function is called and the other span toggles gray/white.
HTML:
<button id='ChangeValue'>item++</button>
<hr/>
<span>Update fired viewModel.item = </span>
<span data-bind='text: $data.item(), bind1: $data.item'></span>
<br/>
<span>Update not fired viewModel.item = </span>
<span data-bind='text: $data.item(), bind2: $data.item'></span>
JS/jQuery/knockoutjs:
$(document).ready(function() {
$('#ChangeValue').click(function() {
viewModel.item(viewModel.item() + 1);
});
var viewModel = {
item: ko.observable(1)
};
ko.bindingHandlers.bind1 = {
init: function(element, valueAccessor) {
},
update: function(element, valueAccessor) {
ko.unwrap(valueAccessor());
if (element.style.background == "gray") {
element.style.background = "white";
} else {
element.style.background = "gray";
}
}
};
ko.bindingHandlers.bind2 = {
init: function(element, valueAccessor) {
},
update: function(element, valueAccessor) {
// if uncomment following line "update" is called
//ko.unwrap(valueAccessor());
if (element.style.background == "gray") {
element.style.background = "white";
} else {
element.style.background = "gray";
}
}
};
ko.applyBindings(viewModel);
});
Is this the expected behavior? I have a complex update functionality in my real code and was wondering why some of my binding handlers are not firing and after a long time of research I did extract this example.
I think it has something to do with the garbage collector in the browser (tested in chrome/IE 11) or knockout.
So my questions:
Is it enought to add "ko.unwrap(valueAccessor());" in my update part of the custom binding to be sure it will be processed?
Is there any documentation about that? Because I did not find something at knockout.js
Yes, this is the expected behavior. The update method is called whenever the binding is applied, and while executing, knockout registers/tracks the observables that are accessed in update method. So in your case, the bind2 binding is not accessing the valueAccessor value, therefore not triggering any update. So if you have
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
This enables knockout to trigger update on any change in valueAccessor() observable.
If you access the value of any observable in update function, the change in that observable will trigger the update function.
It is documented well here.
Using: http://knockoutjs.com/downloads/knockout-2.1.0.js
http://jsfiddle.net/fgav467w/1/
ko.bindingHandlers.bind1 = {
init: function(element, valueAccessor) {
},
update: function(element, valueAccessor) {
if (element.style.background == "gray") {
element.style.background = "white";
} else {
element.style.background = "gray";
}
}
};
The 3.0.0 releases removed a lot of extra "work" which Dandy describes. The 2.0.0 releases like the one I use in the fiddle did not. They will work as you intend but the overall performance will not be as good. But you have options at least.
I use select2 and knockoutJs with this simple binding:
ko.bindingHandlers.select2 = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var options = ko.toJS(valueAccessor()) || {};
setTimeout(function () {
$(element).select2(options);
}, 0);
}
};
Markup:
<select class="select2" style="width:100%" data-bind="optionsCaption: '',options: $root.items,optionsText: 'description',optionsValue: 'id', value: $root.selectedItem,select2: { placeholder: 'Select an item...',allowClear: true }"></select>
it works! Now I enabled the allowClear option in Select2 to clear dropdown to a placeholder value like Select an item....
If I click the x icon dropdown properly sets the placeholder but knockout don't update observable binded value!
I think I've to change custombinding adding something like this:
setTimeout(function () {
$(element).select2(options).on("select2-removed", function (e) {
ko.bindingHandlers.value.update(element, function () { return ''; });
});
...
but it won't work!
There are couple of issues.
1) the update in bindinghandler is to update DOM based on value change, you should never define an update callback with the ability to mutate your model.
The right way is, when define a new bindinghandler, use init callback to hook up all change events with the model, the update callback is merely a DOM drawing routine.
If your init provided DOM drawing (such as provided by select2), you don't need to define an update callback.
Hence the line ko.bindingHandlers.value.update(element, function () { return ''; }); only redraw the DOM, it doesn't do what you want.
2) the select2 binding you created has some holes.
first, value binding doesn't know the existence of select2 binding, that's where you struggled.
second, your select2 binding has to wait for other binding (the options binding) to finish DOM creation, what's why you use setTimeout. But ko provided a way to define sequence of the bindings, just look the source code of ko value binding, it's defined as 'after': ['options', 'foreach']
third, your select2 doesn't respond to outside change. For instance, if you have another UI to update $root.selectedItem (a normal select list), the change raised by that UI would not sync back to your select2.
The solution
Define select2 binding based on existing value binding (just found out it's not needed), and hook up all change events.
we don't need "select2-removed" event, it's all about "change" event.
select2 provided all drawing, we don't need update callback.
use a flag shouldIgnore to break the loop between value subscriber and select2 change event handler.
http://jsfiddle.net/huocp/8N3zX/6/
http://jsfiddle.net/huocp/8N3zX/9/
ko.bindingHandlers.valueSelect2 = {
'after': ['options'],
'init': function(element, valueAccessor, allBindings) {
// kind of extend value binding
// ko.bindingHandlers.value.init(element, valueAccessor, allBindings);
var options = allBindings.get('select2Options') || {};
$(element).select2(options);
var value = valueAccessor();
// init val
$(element).val(ko.unwrap(value)).trigger("change");
var changeListener;
if (ko.isObservable(value)) {
var shouldIgnore = false;
changeListener = value.subscribe(function(newVal) {
if (! shouldIgnore) {
shouldIgnore = true;
// this is just select2 syntax
$(element).val(newVal).trigger("change");
shouldIgnore = false;
}
});
// this demo only works on single select.
$(element).on("change", function(e) {
if (! shouldIgnore) {
shouldIgnore = true;
if (e.val == '') {
// select2 use empty string for unselected value
// it could cause problem when you really want '' as a valid option
value(undefined);
} else {
value(e.val);
}
shouldIgnore = false;
}
});
}
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
if (changeListener) changeListener.dispose();
$(element).select2("destory");
});
}
};
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 the update 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.
ko.bindingHandlers.dialog = {
init: function(element, valueAccessor, allBindingsAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {};
//do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom
setTimeout(function() {
options.close = function() {
allBindingsAccessor().dialogVisible(false);
};
$(element).dialog(options);
}, 0);
//handle disposal (not strictly necessary in this scenario)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).dialog("destroy");
});
},
update: function(element, valueAccessor, allBindingsAccessor) {
var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().dialogVisible),
$el = $(element),
dialog = $el.data("uiDialog") || $el.data("dialog");
//don't call open/close before initilization
if (dialog) {
$el.dialog(shouldBeOpen ? "open" : "close");
}
}
};
Used like:
<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Dialog test' }, dialogVisible: isOpen">foo dialog</div>
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
setTimeout(function() {
options.close = function() {
allBindingsAccessor().dialogVisible(false);
};
$(element).dialog(ko.toJS(options));
}, 0);
and check for observables on update
//don't call dialog methods before initilization
if (dialog) {
$el.dialog(shouldBeOpen ? "open" : "close");
for (var key in options) {
if (ko.isObservable(options[key])) {
$el.dialog("option", key, options[key]());
}
}
}
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 the init 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:
ko.bindingHandlers.jqDialogTitle = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).dialog('option', 'title', value);
}
};
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.
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 a with 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 like editedPerson, and create a div with edition controls, with a binding like this:
data-bind="with: editedPerson"
When you add a person to the observable lke so:
vm.editedPerson(personToEdit);
the binding makes the div visible. When you finish the edition, you can set the observable to null, like so
vm.editedPerson(null);
and 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:
data-bind="with: editedPerson, withDialog: {/* jQuery UI dialog options*/}"
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).
// Variation on Niemeyer's http://jsfiddle.net/rniemeyer/SnPdE/
/*
This binding works in a simple way:
1) bind an observable using "with" binding
2) set the dialog options for the ui dialog using "withDialog" binding (as you'd do with an standard jquery UI dialog) Note that you can specify a "close" function in the options of the dialog an it will be invoked when the dialog closes.
Once this is done:
- when the observable is set to null, the dialog closes
- when the observable is set to something not null, the dialog opens
- when the dialog is cancelled (closed with the upper right icon), the binded observable is closed
Please, note that you can define the defaults for your binder. I recommend setting here the modal state, and the autoOpen to false.
*/
ko.bindingHandlers.withDialog = {
init: function(element, valueAccessor, allBindingsAccessor) {
var defaults = {
modal: false,
autoOpen: false,
};
var options = ko.utils.unwrapObservable(valueAccessor());
//do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom
$.extend(defaults, options)
setTimeout(function() {
var oldClose = options.close;
defaults.close = function() {
if (options.close) options.close();
allBindingsAccessor().with(null);
};
$(element).dialog(defaults);
}, 0);
//handle disposal (not strictly necessary in this scenario)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).dialog("destroy");
});
},
update: function(element, valueAccessor, allBindingsAccessor) {
var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().with),
$el = $(element),
dialog = $el.data("uiDialog") || $el.data("dialog");
//don't call open/close before initilization
if (dialog) {
$el.dialog(shouldBeOpen ? "open" : "close");
}
}
};
var person = function() {
this.name = ko.observable(),
this.age = ko.observable()
}
var viewModel = function() {
label= ko.observable('dialog test');
editedPerson= ko.observable(null);
clearPerson= function() {
editedPerson(null);
};
newPerson= function() {
editedPerson(new person());
};
savePerson= function() {
alert('Person saved!');
clearPerson();
};
return {
label: label,
editedPerson: editedPerson,
clearPerson: clearPerson,
newPerson: newPerson,
savePerson: savePerson,
};
}
var vm = viewModel();
ko.applyBindings(vm);
.header {
font-size: 16px;
font-family: sans-serif;
font-weight: bold;
margin-bottom: 20px;
}
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" rel="stylesheet"/>
<h1 class="header" data-bind="text: label"></h1>
<div id="dialog" data-bind="with: editedPerson, withDialog: {autoOpen: false, title: 'Dialog test', close: function() { alert('closing');} }">
Person editor<br/>
Name:<br/><input type="text" data-bind="value: $data.name"/><br/>
Age:<br/><input type="text" data-bind="value: $data.age"/><br/>
<button data-bind="click: $parent.savePerson">Ok</button>
<button data-bind="click: $parent.clearPerson">Cancel</button>
</div>
<div>
<button data-bind="click: clearPerson">Clear person</button>
<button data-bind="click: newPerson">New person</button>
</div>
<hr/>
<div data-bind="text: ko.toJSON($root)"></div>
There's now this library that has all the JQueryUI bindings for KnockoutJS including, of course, the dialog widget.