Nested GSAP draggables not updating on knockout update - javascript

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>

Related

Update element text using custom binding

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 :-)

show underlying value of input on focus knockout custom binding

I have created (please excuse the mess - just hacking it together at the moment!) the following binding handler in knockout
The desired functionality is that if a field is numeric then it will be formatted to 'X' decimal places (default 2) when displaying but the underlying value is whatever is entered.
All works fine, but I want to add the functionality that when an input is focused then it shows the actual value and I just have no idea how to do this.
i.e.
user enters 1.1121 into an input
when they leave the input it formats to 1.11
if they go back into the input (focus) then it displays 1.1121 for editing
It is point 3 that I have no idea how to achieve as at the moment it shows 1.11 and then over-writes on blur??
Can anyone point me in the right direction - basically where do I access the underlying value on focus and replace the displayed text with the underlying value??
for brevity I have removed some other 'decoration' code that wraps the inputs as they are not relelvant.
Thanks in advance.
ko.bindingHandlers.specialInput = {
init: function (element, valueAccessor, allBindingsAccessor) {
var value = valueAccessor();
var decimals = allBindingsAccessor().decimals || 2;
var formatString = "";
var interceptor = ko.computed({
read: function () {
if(isNumeric(ko.unwrap(value))){
//to do if time - replace this with a function that will accept any number of decimals
if(decimals == 0){
formatString = "0,0";
}
if(decimals == 1){
formatString = "0,0.0";
}
if(decimals == 2){
formatString = "0,0.00";
}
if(decimals == 3){
formatString = "0,0.000";
}
if(decimals == 4){
formatString = "0,0.0000";
}
if(decimals == 5){
formatString = "0,0.00000";
}
return numeral(ko.unwrap(value)).format(formatString);
}else{
return ko.unwrap(value);
}
},
write: function (newValue) {
if ($.trim(newValue) == '')
value("");
else
if(isNumeric(newValue)){
value(numeral().unformat(newValue));
value.valueHasMutated();
}else{
value(newValue);
value.valueHasMutated();
}
}
}).extend({notify: 'always'});
if (element.tagName.toLowerCase() == 'input') {
ko.applyBindingsToNode(element, {
value: interceptor
});
} else {
ko.applyBindingsToNode(element, {
text: interceptor
});
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var value = ko.unwrap(valueAccessor());
return ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
}
};
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
I wouldn't implement this with an interceptor computed.
I'd rather do the following:
In the init register event handlers for focus and blur in the init:
on the focus handler, you have to show the underlying value in the input
on the blur handler, you have to store the number in the textbox, and show the rounded value in the input
In the update you have to store the real value, and show it in the textbox. Most probably the textbox won't have the focus when you update the value, so you should be safe showing the rounded value. However, if you think that in some cases it can be focused, you can do something like this to test it to decide how to show the value: Using jQuery to test if an input has focus
pseudocode
ko.bindingHandlers.yourBindingName = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var $element = $(element);
$element.on('focus', function() {
// Show raw value:
$element.val(/* raw value */);
});
$element.on('blur', function() {
// Update the observable with the value in the input
ko.unwrap(valueAccessor())( /* get raw value from input */);
// Show the rounded value
$element.val(/* rounded value */);
});
// Update will be called after init when the binding is first applied,
// so you don't have to worry about what it's initially shown
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// When the value changes, show it in the input
$.element.val(/* if focused show raw, if not, show roundede*/);
}
};
If you are sure that you are only going to use it with input and writable observables, this code is safe. If you have doubts about it, you should add many checks like cheking the kind of element to use jQuery .val() or .text(), check if the binding expression is a writeable observable to update its value, and so on.
NOTE: thereis something which is overseen many times: disposing of objects that we no longer need when the element is destroyed (that can happen for example with 'if', 'whit' or 'template'). In this case I think you don't need to do that, but, please, see this: Custom disposal logic if you think you need to destroy something.
There's nothing wrong with using jQuery in a binding handler, but you could also do this pretty simply with existing binding handlers. It's the sort of thing that could be rolled into a component to be re-used (although I didn't do that in my example).
You just need a backing store for your value, and the computed returns either the backing store or the formatted backing store, depending on whether the input has focus (using the hasFocus binding).
vm = (function() {
var editing = ko.observable(false),
backingStore = 0,
value = ko.computed({
read: function() {
return editing() ? backingStore : (+backingStore).toFixed(2);
},
write: function(newValue) {
backingStore = newValue;
}
});
return {
editing: editing,
value: value
}
}());
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="hasFocus: editing, value: value" />
<input />

knockoutjs Binding Handler only firing when ko.unwrap(valueAccessor()); is in update: part

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.

Animate on changing the value of an observable in Knockout

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/

How to create a custom binding for jquery ui dialog? [duplicate]

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.

Categories