Binding keypress event on knockoutjs, observable not populated - javascript

Need a little help with knockoutjs and binding a keypress event. I'm trying to hook up knockout so that I pick up on the enter keypress from within a text box. So I can perform the same action as clicking a button. Its a little tricky to explain but hopefully this JsFiddle will demonstrate what I'm trying to achieve.
http://jsfiddle.net/nbnML/8/
The problem I have is that observable value is not getting updated and I think its something to do with an observable not being updated until focus moves away from the textbox?
Any solutions to this problem.
Thanks!

One option is to use the valueUpdate additional binding to force an update on each keypress. For example, you would do:
<input type="text" data-bind="value: InputValue, valueUpdate: 'afterkeydown', event: { keypress: RunSomethingKey }" />
If that is not what you are after, then really you would want to fire the element's change event in your handler. For example with jQuery, you would do something like: $(event.target).change();.
It would be better though to move this into a custom binding. Maybe something like (probably should check if the result of valueAccessor() is a function):
ko.bindingHandlers.enterKey = {
init: function(element, valueAccessor, allBindings, vm) {
ko.utils.registerEventHandler(element, "keyup", function(event) {
if (event.keyCode === 13) {
ko.utils.triggerEvent(element, "change");
valueAccessor().call(vm, vm); //set "this" to the data and also pass it as first arg, in case function has "this" bound
}
return true;
});
}
};
Here is your sample updated: http://jsfiddle.net/rniemeyer/nbnML/9/

Don't discount submit bindings:
http://knockoutjs.com/documentation/submit-binding.html
This takes care of some IE 9/10 gotchas such as the return key not updating the observable. With this taken care of you don't need to intercept keycode 13
html:
<form data-bind="submit:RunSomething">
<input type="text" data-bind="value: InputValue" />
<input type="submit" value="test" />
<div data-bind="text: InputValue" />
</form>
code:
var ViewModel = function () {
var self = this;
self.InputValue = ko.observable('');
self.RunSomething = function (ev) {
window.alert(self.InputValue());
}
}
ko.applyBindings(new ViewModel());
See this here:
http://jsfiddle.net/jnewcomb/uw2WX/

Related

Knockout: How to pass the Checkbox "checked" value to a Click handler function?

I want to send "true/false" value to a click handler, depending if the checkbox is checked or not.
On check, it should send a 3rd parameter (isChecked) with value "true".
On uncheck, it should send a 3rd parameter (isChecked) with value "false".
I'm sure this is really easy, but I'm having a hard time figuring it out.
Here is the input element:
<input class="cards-view--item-checkbox pull-right" type="checkbox"
data-bind="value: universalParcelId, checked: $parent.isChecked, checkedValue: true,
click: function(data, event, isChecked) {
return $root.addUPIDtoArray(data, event, $parent.isChecked()) }">
Click handler:
addUPIDtoArray: function (data, event, isChecked) {
var self = this;
self.isChecked = ko.observable();
// If checked
if(isChecked()) {
self.upIDArray.push(data.universalParcelId);
self.upIDWithIndexArray.push({
universalParcelID: data.universalParcelId,
searchResultIndex: data.searchResultIndex
});
// If unchecked
} else if(!isChecked()) {
// remove from array
}
return true; // allow the default "click" action, which is checking the box with a "check"
},
I thought i could use the "event" parameter, but for some reason it is coming through as a jQuery.event, instead of a regular DOM event. So I decided for the 3rd parameter. But it just doesn't work like this: gives error $parent.isChecked is not a function
Any ideas?
I got it to work by utilizing $element.checked and passing that as a parameter to my click handler function
<input style="display: none;" class="cards-view--item-checkbox pull-right" type="checkbox"
data-bind="value: universalParcelId, checked: $parent.isChecked, click: function(data, event) {
return $root.addUPIDtoArray($element.checked, data, event) }">
It might not be "best practice" but it works! What objections are there to doing it this way?
Unless you need to distinguish a click from some other way of setting the variable in the checked binding, you don't want a click handler. You just want to subscribe to the variable, which will execute your function whenever its value changes.
You've written your click binding as if adding the parameter to the parameter list will make Knockout know what to pass it. You'll want to re-think that. Generally, it's best to write your click handler as a member of your ViewModel and just make the binding like click: methodName.
Below is an example of a click binding on a checkbox. There's an interval toggling the checkbox each second. That won't trigger the click binding.
There is also a subscription that counts the times the value has changed, and what the last value was.
vm = {
box: ko.observable(true),
changes: ko.observable(0),
lastChange: ko.observable(''),
stuff: ko.observableArray(),
doThing: function() {
vm.stuff.push(vm.box() ? 'checked' : 'not');
return true;
}
};
vm.box.subscribe(function(newValue) {
vm.changes(vm.changes() + 1);
vm.lastChange(newValue ? 'checked' : 'not');
});
ko.applyBindings(vm);
// This will change the checkbox without firing the click
setInterval(function() {
vm.box(!vm.box());
}, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="checkbox" data-bind="checked: box, click: doThing" />
<div>Changes: <span data-bind="text:changes"></span>, last:<span data-bind="text:lastChange"></span>
<div data-bind="foreach:stuff">
<div data-bind="text: $data"></div>
</div>

angularJS: how to revert / prevent radio button event?

i have a (non-working) example in http://jsfiddle.net/S2kc7/1/
<radio ng-model="value" ng-value="foo">
<radio ng-model="value" ng-value="bar">
if a user does NOT approve, i'd like to revert to the previous state.
e.g., if ng-model="value" was on "foo" before the user clicked on "bar", and then the user chose to cancel, i'd like to prevent that event, and remain on "value=foo", without anything getting changed or $watched.
I tried:
$scope.$watch('value', function(){ newvalue = oldvalue })
$scope.clicked = function($event) { $event.preventDefault(); }
<radio ng-change="check_and_prevent()">
none of these methods were able to cancel the event (in my humle tests). some of the remains of the tests are commented out in the jsfiddle above.
can i prevent event on <radio>?
can i prevent event on <select> ?
EDIT
#jose's answer worked for the case presented, but not in the real website;
In my website, "value" is actually a property of an object; but even that works out in jsFiddle's sterile environment: http://jsfiddle.net/L5555/
but not in my website.
I can't tell what's the difference, and i can't reveal my website.
thanks anyway.
The accepted answer does not covers scenerios which makes ajax calls on model change.
You can completely prevent changes by surrounding radio inputs with label tag.
<label ng-click="confirmChange($event)"><input type="radio" ng-model="value" value="foo" ></label>
$scope.confirmChange = function(event) {
if(!confirm('sure?')) {
event.preventDefault();
}
};
You can make it work by using ng-change. Make sure that each radio has the ng-change on it
<input type="radio" ng-model="value" value="foo" ng-change="validateChange()">
<input type="radio" ng-model="value" value="bar" ng-change="validateChange()">
<input type="radio" ng-model="value" value="zaz" ng-change="validateChange()">
And you can use this logic in your controller
$scope.value= $scope.preval= 'foo';
$scope.validateChange = function() {
if(confirm('revert??? value='+$scope.value+' preval='+$scope.preval)) {
$scope.preval = $scope.value;
} else {
$scope.value = $scope.preval;
}
};
Updated and working fiddle http://jsfiddle.net/S2kc7/3/

Backbone.js: capture custom events on jQuery plugin?

I have a Backbone view for a search form. The form includes several elements, including a slider implemented in jSlider. I want to capture all changes to the form, and update the results shown accordingly.
I can capture the click events fine, but I don't know how to capture the slide event on jSlider - I know how to bind custom events in Backbone, but jSlider seems not to have a way to bind it directly.
Here's my HTML:
<form id="searchform">
<input type="text" id="city" />
<input type="text" id="type" />
<input id="price-jslider" type="slider" name="price" value="1;500" />
</form>
And here's my Backbone code:
var SearchFormView = Backbone.View.extend({
el: $('#searchForm'),
events: {
"click input": "updateResults",
// how to capture slide event on jslider?
},
updateResults: function(e) {
// do stuff
}
});
Does anyone have any ideas on how to capture this sort of event?
You can pass an onstatechange function when initializing the jQuery slider:
var SearchFormView = Backbone.View.extend({
el: $('#searchForm'),
render: function() {
var self = this;
$("#price-jslider").slider({
onstatechange: function( value ) {
self.updateResults();
}
});
},
events: {
"click input": "updateResults"
},
updateResults: function(e) {
// do stuff
}
});
from jslider page
onstatechange function(value)
Function fires while slider change state.
callback function(value)
Function fires on "mouseup" event.
when you instantiate jslider, you define those function in option
$(whatever).jslider({
onstatechange : function(v) {
//set your input value to v
$('#searchForm').trigger('click')
}
})

jQuery event binding does not work properly or i can't make it properly working

HTML:
<input id="email" name="email" type=text />
<input id="password name="password" type="password" />
JavaScript:
var fields = ["email","password"];
for (var i in fields) {
var field = $("#"+fields[i]);
field.bind({
focus: function() {
field.css("border-color","#f00");
},
blur: function() {
field.css("border-color","#000");
}
});
}
My desire action will be as follows:
When I place cursor on any of the above fields the input field's border will be red.
When I take away the cursor from the field it's border will be black.
But the event is occurs only for the password filed whether I place and take away cursor form any of the above fields.
This is a really common problem, the field variable accessed on the focus and blur event belongs to the outer scope, so it contains the last iterated value, in your case it will contain "password".
There are a lot of ways to solve this, for example you could use $.each which introduces a new scope:
jQuery.each(["email", "password"], function(index, fieldId) {
var field = $('#'+fieldId);
field.bind({
focus: function() {
field.css("border-color","#f00");
},
blur: function() {
field.css("border-color","#000");
}
});
});
Or using $(this) instead of field in your event handlers, i.e.:
var fields = ["email","password"];
for (var i = 0; i < fields.length; i++) {
$("#"+fields[i]).bind({
focus: function() {
$(this).css("border-color","#f00");
},
blur: function() {
$(this).css("border-color","#000");
}
});
}
Off-topic note: also in the above example that I use a normal loop, instead the for...in statement, which is not really meant to be used on array-like objects.
Try out the two code examples here.
This works:
$('input#email, input#password').bind({
focus: function() {
$(this).css("border-color","#f00");
},
blur: function() {
$(this).css("border-color","#000");
}
});
You may also need to clean up your HTML if the typos in your question are in the real thing:
<input id="email" name="email" type="text" />
<input id="password" name="password" type="password" />
One additional note that could improve your code. jQuery does almost everything by iterating over sets, so there is no need to manually iterate over a list of keys with a for loop. Instead, you can just do this:
$("#email, #password").bind({ // grab both elements at once
focus: function() {
$(this).css("border-color","#f00");
},
blur: function() {
$(this).css("border-color","#000");
}
});
Note that, as in CMS's example, I am using this to reference the element within the handler functions. this will refer to the node on which the event was triggered, so it will be #email when focusing in that field, and #password when focusing on the other.o

How to call my onchange function after my blur function in javascript

I have a blur function already attached to my dropdown, now I want to call another function onchange after my blur function is called in javasript.
<select onchange="CheckAccommodation(this)" onblur="return UpdateFormSelect('UpdatePrice.aspx', 'BookNow_accommodation1', 'select', 'BookNow_accommduration');javascript:test()" id="BookNow_accommodation1" name="BookNow:accommodation1">
Now I want to call my javascript:test() after the blur is done
Please suggest!
Thanks.
Best Regards,
Yuv
This suggested by #Ghommey will work:
<select onchange="CheckAccommodation(this)" onblur="return
UpdateFormSelect('UpdatePrice.aspx', 'BookNow_accommodation1',
'select', 'BookNow_accommduration');test()" id="BookNow_accommodation1"
name="BookNow:accommodation1">
it works irrespective of what you return from UpdateFormSelect.
function onChangeHandler(){
//.........
}
$("SELECT_ID").blur(function(){
//handle blur
//call on change handler
onChangeHandler();
});
Now I want to call my javascript:test() after the blur is done
Then just remove the ‘return’ on the first statement, allowing it to fall through to the second:
onblur="UpdateFormSelect('UpdatePrice.aspx', 'BookNow\_accommodation1', 'select', 'BookNow\_accommduration');test()"
You could put return test(), but actually onblur doesn't need to return anything. <a onclick> often needs to return false to stop the link being followed, but other than that you often don't need any return value from an event handler.
It's also a bit of a mouthful to put in an event handler. You might benefit by breaking the JavaScript out:
<select id="BookNow_accommodation1" name="BookNow:accommodation1">
...
</select>
<script type="text/javascript">
var acc1= document.getElementById('BookNow_accommodation1');
acc1.onchange= function() {
CheckAccommodation(this);
// or just move the body of CheckAccommodation here
};
acc1.onblur= function() {
UpdateFormSelect('UpdatePrice.aspx', 'BookNow_accommodation1', 'select', 'BookNow_accommduration');
test();
};
</script>

Categories