integrating jquery ui dialog with knockoutjs - continued - javascript

Hi this is a continuation to this question here:
integrating jquery ui dialog with knockoutjs
I have implemented Niemeyers solution for a modal to handle login but would like it to handle a create-user page as well. This is the first time I'm writing a custom handler and don't really know how to continue from here..
Is this possible to implement in the same handler or do I need to create two separate handler for each page?
Anyways there are some comments in there on how I tried to approach this, but could use a helping hand on this..
Link to fiddle: http://jsfiddle.net/king_s/9m3678m2/6/
HTML
<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Sign in' }, signIn_Visible: isOpen">
<table>
<tr>
username
<input />
</tr>
<tr>
password
<input />
</tr>
<tr>
sign up
</tr>
</table>
<button data-bind="click: close">Close</button>
</div>
<!--Mark up for dialog page 2
<div id="dialog_two" data-bind="dialog: {autoOpen: false, title: create user }", create_visible>
MARK UP GOES HERE..
</div>
-->
<div>
<button data-bind="click: open">Open</button>
<button data-bind="click: close">Close</button>
</div>
<hr/>
<div data-bind="text: ko.toJSON($root)"></div>
JS
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().signIn_Visible(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().signIn_Visible),
$el = $(element),
dialog = $el.data("uiDialog") || $el.data("dialog");
// var openRegistration = ko.utils.unwrapObservable(allBindingsAccessor()
// .create_visible),
// $ele = $(element),
// dialog_reg = $el.data("uiDialog") || $el.data("dialog");
//don't call open/close before initilization
if (dialog) {
//open or close - functions on the jquery dialog object
$el.dialog(shouldBeOpen ? "open" : "close");
//Create conditional statement here to decide wheter to open/closes this page
//or navigate to page 2??
}
}
};
var viewModel = {
label: ko.observable('dialog test'),
isOpen: ko.observable(false),
open: function() {
this.isOpen(true);
},
close: function() {
this.isOpen(false);
}
};
ko.applyBindings(viewModel);

Related

Getting Error: You cannot apply bindings multiple times to the same element in OJET

Below is my HTML code to open oj-dialog
<div id="dialogWrapper">
<div style="display:none" id="modalDialog1" title="Modal Dialog" data-bind="ojComponent:{component: 'ojDialog',
initialVisibility: 'hide',
open: dialogOpen,
close: dialogClose,
rootAttributes: {
class: 'animate'
}}">
<div class="oj-dialog-body">
The dialog window can be moved, resized and closed with the 'x' icon.
Arbitrary content can be added to the the oj-dialog-body and oj-dialog-footer sections.
</div>
<div class="oj-dialog-footer">
<button id="okButton" data-bind="click: closeDialog,
ojComponent: {component: 'ojButton',
label: 'OK'}"> </button>
</div>
</div>
<button id="buttonOpener" data-bind="click: openDialog,
ojComponent: {component: 'ojButton',
label: 'Open Modal Dialog'}"></button>
</div>
Below is my corresponding js code :
define(['require', 'exports', 'knockout', 'ojs/ojbootstrap', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojdialog'],
function(ko, oj, $) {
'use strict';
var ViewModel = function() {
var self = this;
self.openDialog = function() {
$("#modalDialog1").ojDialog("open");
};
self.closeDialog = function(data, event) {
// attempt fadeOut with jQuery
$(event.target)
.closest(".oj-dialog.animate")
.fadeOut(function() {
console.log("faded out", this);
$("#modalDialog1").ojDialog("close");
});
};
self.dialogOpen = function(event, ui) {
$(event.target)
.closest(".oj-dialog.animate")
.addClass("fully-opaque");
};
self.dialogClose = function(event, ui) {
$(event.target)
.closest(".oj-dialog.animate")
.removeClass("fully-opaque");
};
};
ko.applyBindings(new ViewModel());
});
When I run this code I am getting "Error: You cannot apply bindings multiple times to the same element." error. I am new to development and as per document I have added all libraries. Can Anyone see where its going wrong.

Can not open jQuery UI dialog multiple times - test case included

I have prepared a simple test case demonstrating my problem -
I use a jQuery UI button to open a jQuery UI dialog:
However this works only once. On subsequent button clicks I get the error:
Uncaught Error: cannot call methods on dialog prior to initialization;
attempted to call method 'open'
Even though I do initialize the dialog before the button in my code -
HTML-code:
<BUTTON ID="newBtn">New game</BUTTON>
<DIV ID="newDlg" TITLE="New game">
Select game board:
<BUTTON value="1">Winter</BUTTON>
<BUTTON value="2" DISABLED>Spring</BUTTON>
<BUTTON value="3" DISABLED>Summer</BUTTON>
<BUTTON value="4" DISABLED>Autumn</BUTTON>
</DIV>
JavaScript:
var newDlg = $('#newDlg').dialog({
modal: true,
autoOpen: false,
close: function(e, ui) {
var bid = parseInt($(this).data('bid'));
$(this).removeData();
if (1 <= bid && bid <= 4) {
alert('selected board id: ' + bid);
}
},
buttons: {
'Close': function() {
$(this).dialog('close');
}
}
});
$('#newDlg button').button().click(function(e) {
e.preventDefault();
var bid = this.value;
newDlg.data('bid', bid);
newDlg.dialog('close');
});
var newBtn = $('#newBtn').button().click(function(e) {
e.preventDefault();
newDlg.dialog('open'); // also tried $('#newDlg') here!
});
I have tried using jQuery UI 1.11.4 and 1.12.1 but the problem persists.
Funny story, the issue is:
$(this).removeData();
This is removing all data attributes, including those used to define the dialog. This can be fixed by defining the specific data you want to remove:
$(this).removeData('bid');
Then it works as expected.
Forked working example: https://jsfiddle.net/Twisty/dz8krbye/

Nested GSAP draggables not updating on knockout update

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>

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.

Prevent a double click on a button with knockout.js

What is the best way to disable a button so a double click doesn't occur with knockout.js. I have some users doing some quick clicking causing multiple ajax requests. I assume knockout.js can handle this in several ways and wanted to see some of the alternatives out there.
Use a semaphore (spinning lock). Basically, you count how many clicks an element has registered and if it is more than 1 you return false and don't allow the following clicks. A timeout function could be used to clear the lock so that they could click again after say, 5 seconds. You could modify the example from http://knockoutjs.com/documentation/click-binding.html
As seen here:
<div>
You've clicked <span data-bind="text: numberOfClicks"></span> times
<button data-bind="click: incrementClickCounter">Click me</button>
</div>
<script type="text/javascript">
var viewModel = {
numberOfClicks : ko.observable(0),
incrementClickCounter : function() {
var previousCount = this.numberOfClicks();
this.numberOfClicks(previousCount + 1);
}
};
</script>
By changing the logic inside the nested function to
if( this.numberOfClicks() > 1 ){
//TODO: Handle multiple clicks or simply return false
// and perhaps implement a timeout which clears the lockout
}
I ran into a similar problem with a form wizard submitting data via Ajax on button click. We have 4 buttons selectively visible for each step. We created a boolean observable ButtonLock and returned from the submission function if it was true. Then we also data-bound the disable of each button to the ButtonLock observable
ViewModel:
var viewModel = function(...) {
self.ButtonLock = ko.observable(false);
self.AdvanceStep = function (action) {
self.ButtonLock(true);
// Do stuff
// Ajax call
}
self.AjaxCallback = function(data) {
// Handle response, update UI
self.ButtonLock(false);
}
Button:
<input type="button" id="FormContinue" name="FormContinue" class="ActionButton ContinueButton"
data-bind="
if: CurrentStep().actions.continueAction,
disable: ButtonLock,
value: CurrentStep().actions.continueAction.buttonText,
click: function() {
AdvanceStep(CurrentStep().actions.continueAction);
}"/>
If you just need to prevent multiple clicks, I prefer the boolean. But the counter method lets you detect double clicks and handle them separately, if you want that feature.
In case anyone is still looking for a way to do this. I found that You can use a boolean.
self.disableSubmitButton= ko.observable(false);
self.SubmitPayment = function () {
self.disableSubmitButton(true);
//your other actions here
}
Then in your view
data-bind="click:SubmitPayment, disable:disableSubmitButton"
I did this with a custom binding:
<button data-bind="throttleClick: function() { console.log(new Date()); }>
I wont double click quicker than 800ms
</button>
ko.bindingHandlers.throttleClick = {
init: function(element, valueAccessor) {
var preventClick = false;
var handler = ko.unwrap(valueAccessor());
$(element).click(function() {
if(preventClick)
return;
preventClick = true;
handler.call();
setTimeout(function() { preventClick = false; }, 800);
})
}
}

Categories