This should be simple but certainly I'm getting it wrong.
How to update ko.observable text on the click event?
I could do this using "afterkeydown" or "keypress" but not in case click event.http://knockoutjs.com/documentation/value-binding.html
<span data-bind="text: Count"></span>
<button data-bind="click: update">Update</button>
function MyViewModel() {
var self = this;
self.Count = ko.observable("0");
self.update = function() {
self.Count = ko.observable("1");
}
}
http://jsfiddle.net/EBsj5/
You should change it like a function.
self.update = function() {
self.Count("1");
}
Demo: http://jsfiddle.net/EBsj5/1/
Any basic tutorial will explain this to you, so I recommend watching a few.
When you are setting the value of a Knockout observable, you need to use parans like a function and pass in the new value.
<span data-bind="text: Count"></span>
<button data-bind="click: update">Update</button>
function MyViewModel() {
var self = this;
self.Count = ko.observable("0");
self.update = function() {
self.Count("1");
}
}
This will update the observable to "1" in this case. You don't need to call ko.observable() again because you have already created the observable, you are simply trying to 'set' the value with the setter function.
Related
Here is my html looks like:
<div class="xyz-template" data-bind="html:myViewModel().htmlTemplate"></div>
And I would like to have data-bind in htmlTemplate itself:
here is my knockout:
function myViewModel() {
self.htmlTemplate = ko.observable();
self.initialize = function ()
{
self.htmlTemplate(`
<p data-deId="de-box-5" data-bind="html: myViewModel().getDynamicContent()">
Initial content
</p>
`);
};
self.getDynamicContent = function()
{
return "dynamic content";
};
};
Well the return value is
Initial content
How can I have inner bind in binding html?
Whole trick is around rebinding your view. ko does not allow to call applyBindings twice on the same node, so you need to cleanNode from bindings and apply it to element.
Here is the working scenario:
function myViewModel() {
let self = this
self.htmlTemplate = ko.observable();
self.initialize = function ()
{
self.htmlTemplate(`
<p data-deId="de-box-5" data-bind="html: myViewModel().getDynamicContent()">
Initial content
</p>
`);
};
self.getDynamicContent = function()
{
return "dynamic content";
};
return self;
};
const vm = myViewModel();
const root = $('.xyz-template');
ko.applyBindings(vm, root[0]);
vm.initialize();
const templateHolder = root.find('p');
ko.cleanNode(templateHolder[0]);
ko.applyBindings(vm, templateHolder[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="xyz-template" data-bind="html: htmlTemplate"></div>
Keep in mind that in your particular case you cannot rebind root because everytime you initialize binding html binding from xyz div kicks in, so again <p> is somehow detached
Here you will also find better ways to solve that problem knockout custom html binding
Code which I posted is just to show the concept
I have durandal widget and two syncfusion buttons:
view.html
<div class="group-btn-container">
<input type="checkbox" data-bind="ejToggleButton: toggleButtonNo" />
<input type="checkbox" data-bind="ejToggleButton: toggleButtonYes" />
</div>
viewmodel.js
define(['knockout', 'jquery','web/ej.togglebutton.min', 'common/ej.widget.ko.min'], function (ko, $) {
var ctor = function () { };
ctor.prototype.activate = function (settings) {
self = this;
var toggleBtnYes = {
toggleState: ko.observable(false),
click: function (args) {
self.toggleButtonNo.buttonOptions.toggleState(false);
args.model.toggleState(true);
}
}
self.toggleButtonYes = toggleBtnYes;
var toggleBtnNo = {
toggleState: ko.observable(true),
click: function (args) {
self.toggleButtonYes.buttonOptions.toggleState(false);
args.model.toggleState(true);
}
}
self.toggleButtonNo = toggleBtnNo;
};
return ctor;
});
Two buttons 'Yes' and 'No', should work like radio buttons, when one is turned on, another should be turned off.
Q1: I have a problem with viewodel context or scope.
Inside click function I do not see 'self', actually self is always some another widget because in one loop I render couple widgets, I do not know why self is not my function ctor ?
Q2: Also when I want to approach to object 'toggleBtnNo' or 'toggleBtnYes' directly from click function, they are not defined. Why I cannot see the objects from global function ctor in my inner, 'click' function?
I just do not know how to approach to the button (on one or other way) from my click function how I can change it state ?
From the code snippet you have provided, it seems you are trying to update the toggleButton’s state by assigning values to the model. With this, the model values is just assigned and is not reflected in ToggleButton control.
Rather, for the ToggleButton control to be updated, you need to update the value in the observable variable (self.toggleButtonNo.toggleState) in the module and call ko.applyBindings method, so that the model values will be updated the ToggleButton control.
Please refer the code snippet provided below.
HTML
<input id="yes" type="checkbox" data-bind="ejToggleButton: {toggleState: toggleButtonYes.toggleState , defaultText: settings.defaultText, activeText: settings.activeText, click: toggleButtonYes.click}" />
<input id="no" type="checkbox" data-bind="ejToggleButton: {toggleState: toggleButtonNo.toggleState, defaultText: settings.defaultText, activeText: settings.activeText, click: toggleButtonNo.click}" />
Script
define('mo1', ['knockout', 'jquery'], function (ko, $) {
return function () {
self = this;
self.settings = {
defaultText: "No",
activeText: "Yes",
};
var toggleBtnYes = {
toggleState: ko.observable(true),
click: function (args) {
self.toggleButtonNo.toggleState(false);
self.toggleButtonYes.toggleState(true);
ko.applyBindings(self);
}
}
self.toggleButtonYes = toggleBtnYes;
var toggleBtnNo = {
toggleState: ko.observable(false),
click: function (args) {
self.toggleButtonYes.toggleState(false);
self.toggleButtonNo.toggleState(true);
ko.applyBindings(self);
}
}
self.toggleButtonNo = toggleBtnNo;
};
});
This was the worst mistake ever. I was looking at that million times and did not notice. I am ashamed.
I did not put 'var' in front of self.
self = this;
should be:
var self = this;
It caused that self goes to the global scope and every time some other widget overwrites value of self.
var ViewModel = function(){
var self = this;
self.Check = function(){
alert('ok');
}
};
$(function () {
ko.applyBindings(new ViewModel());
});
** how to call a function Check() in normal script from knockout**
Just keep a reference to your view model:
var myVM = new ViewModel();
ko.applyBindings(myVM);
Now you can call the function:
myVM.Check();
But if you want to be able to use it outside the doc ready, you'll need to return it from there and assign it to a variable, or use a global variable.
So you could do something like this:
var myVM = new ViewModel();
$(function() {
ko.applyBindings(myVM);
});
//...
myVM.Check();
If you wanted to use this function as an event handler (for example, to handle a click) you'd do something like this (in HTML):
<a data-bind="click: Check">Click me to check!</a>
For other events, you'd use the event binding.
If you want to bind it to a function do this:
js:
var ViewModel = function(){
var self = this;
self.Check = function(){
return "Ok";
}
};
$(function () {
ko.applyBindings(new ViewModel());
});|
markup
<span data-bind="text:Check()" />
See fiddle:
http://jsfiddle.net/P4bP7/
I am setting some setInterval values on my widget's controller code as follows:
define(['durandal/widget'],function (widget) {
var count = 0;
var intervals = [],
ctor = function (element, settings) {
this.settings = settings;
};
ctor.prototype.updateCount = function( ){
var interval = setInterval(function () {
count = count + 1;
return count;
}, 1000);
intervals.push(interval);
}
return ctor;
}
The above code is being run inside a forEach loop inside the view like:
<div data-bind="foreach: {data: settings.items}">
<span class="count" data-bind="text:$parent.updateCount()"></span>
</div>
What I would like to do is call the clearInterval method on all the items in the intervals array when the widget is destroyed or essentially removed from the dom. I know I could do this using the deactivate on a viewModel but from a reusability point of view, I would like the widget itself to handle the clearing of interval. Is there any way I could achieve this with the widget module in Durandal.
For anyone else looking into the same issue, there's a knockout way of achieving the same. Have a look at the following links https://github.com/BlueSpire/Durandal/issues/139 and https://groups.google.com/forum/#!topic/durandaljs/NqUkY-9us2g . The suggestion is to use:
ko.utils.domNodeDisposal.addDisposeCallback(element, callback)
As long as the widget is removed with JQuery's "remove" function, adding a custom event handler on this "remove" function should go like this:
var self = this;
var self.count = 0;
var self.intervals = [];
self.ctor = function (element, settings) {
$(element).on("remove", function () {
$.each(self.intervals, function(index, ival) {
clearInterval(ival);
});
});
this.settings = settings;
};
The problem is that if the widget is removed without JQuery, simply by manipulating the DOM, the event will not be fired. You could then implement the code for the DOMNodeRemoved event, but it's not gonna work for IE...
Edit: if you're using JQuery pre-1.9.1, you might want to check out the other answers to this question.
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);
})
}
}