Here's a jsfiddle to show what the issue is:
http://jsfiddle.net/boblauer/BgvV4/
I'm trying to fire the change event after a text field is updated. Unfortunately, inside the subscribe method, the text box's value hasn't been updated yet, so when I figure the change event, it's fired too soon.
I need to fire the change event because I have 3rd party code that is out of my control that relies on the change event.
Any suggestions?
A simple solution is to wrap your call to $("#text1").change() in a setTimeout with a timeout of 0. That's enough to let knockout do the (synchronous) update to the textbox value before the jquery change handler gets invoked.
I forked your fiddle to demonstrate:
http://jsfiddle.net/SuRYa/1//
If this is something you need to do a lot, a better solution is probably to wrap this behavior in a custom binding where the "update" callback of the binding would fire the jquery change event on the updated element.
bmode is right, a custom binding will do it. Although this answer is a bit late, here is the binding in case it helps anyone subsequently reading this post. It updates the value of the textbox using jQuery - the DOM is now updated in order for Bob's 3rd party code to work - so then it fires the change event.
ko.bindingHandlers.valueAndFireChange = {
update: function(element, valueAccessor) {
var val = ko.unwrap(valueAccessor());
if (val == undefined) return;
$(element).val(val);
$(element).change();
}
};
Here's an updated version of Bob's fiddle showing this in action:
http://jsfiddle.net/BgvV4/17/
I changed the alerts to console.log, so you'll need the console open to see the useful info.
Related
I have an APEX application where there are many drop down items. I've bound change event handlers to them using the bind function of jQuery.
Now when I load the content of a drop-down programmatically using $('#ELEMENT').trigger('apexrefresh'), the drop-down reloads but the change event handler fires automatically.
How do I prevent this from happening? I tried avoiding binding the event handler using bind and instead adding the onChange attribute to the element. The incorrect behaviour was still present.
Here is the skeletal code:
$(document).ready(function()
{
$('#P7021_MSG_DEF').bind('change', function(e)
{
console.log('bound function onChange() msg_def');
updateStartWord();
}
);
});
function updateMsgDef()
{
console.log('function updateMsgDef() ');
$('#P7021_MSG_DEF').one('apexafterrefresh', function()
{
if( $x('P7021_RESTORE_CHK').value == 'Y')
{
setdefault('P7021_MSG_DEF', vJson.msg_def);
}
updateStartWord();
}
).trigger('apexrefresh');
}
In the above code, when the updateMsgDef is called from another function the function updateStartWord() gets called twice - once by updateMsgDef() itself and again by the onChange handler that was bound to P7021_MSG_DEF item.
If anyone could help on this?
Calling $('#ELEMENT').trigger('apexrefresh') is going to trigger the change event. Short of going back to the drawing board altogether, the solution is going to be a hack whatever you do. You could poke about in (and quite possibly break) Oracle's javascript. You could write your own AJAX to populate the select list.
The easiest way might be to check in your onChange event which element currently has focus, eg:
onChange = "if($( document.activeElement).attr('id')=='YOUR_PAGE_ELEMENT')
{ $( document.activeElement).trigger('apexrefresh'); };"
If the user has changed the select list, it should still have focus. There's no guarantee that will work in all browsers, but I think it should be ok in current Chrome and IE versions.
I've been in a similar situation to yours, and have come to accept that if the page logic is too complicated to implement using DAs, maintaining it is likely going to be a nightmare whatever happens. Much as I like "proper" programming, Apex is really all about the declarative controls.
If you've got a checkbox on a form, to which you bind a function on click using Knockout, it appears to override programming control of the checkbox status.
There's a fiddle demonstrating the problem here:
http://jsfiddle.net/Y5Zk8/
Code:
<input type="checkbox" id="thisFails" data-bind="click: $root.Fails" />
<label for="thisFails">This Fails</label>
var SimpleModel = function() {
this.Fails = function() {
alert('clicked');
$('#thisFails').attr('checked', true);
}
};
ko.applyBindings(new SimpleModel());
Now, I'm well aware that if I return true or false from my function, it'll work. But imagine for a moment that I can't do that (there's reasons for this - it's complicated). Why can't I control the value of the box in JQuery?
As per this other stackoverflow answer:
knockout event binding
and summarised as:
Allowing the default action
By default, Knockout will prevent the event from taking any default
action. For example if you use the event binding to capture the
keypress event of an input tag, the browser will only call your
handler function and will not add the value of the key to the input
element’s value. A more common example is using the click binding,
which internally uses this binding, where your handler function will
be called, but the browser will not navigate to the link’s href. This
is a useful default because when you use the click binding, it’s
normally because you’re using the link as part of a UI that
manipulates your view model, not as a regular hyperlink to another web
page.
However, if you do want to let the default action proceed, just return
true from your event handler function.
So the default is that unless you return true, the default event will be prevented.
If it's checked value you want to control you could the following, it's a bit long winded mind you and a definite hack, but it works.
Setup an observable on your viewmodel to hold whether the checkbox is checked or not
self.isChecked = ko.observable(false);
Setup a computed observable on your vm that will be used to bind to the checkbox checked value;
self.isCheckedCp = ko.computed(function(){
return self.isChecked();
});
Bind the computed to the checked attribute in the html:
<input type="checkbox" id="thisFails" data-bind="click: $root.Fails,checked:isCheckedCp" />
Change the Fails function to incorporate a timeout that will run immediately after the fails function completes, and in that function, set the value of the underlying isChecked observable (in the example it just toggles the current value)
self.Fails = function(e) {
console.log('e',e.isChecked());
alert('arse');
console.log( $('#thisFails'));
setTimeout(function(){
console.log('set now');
//this works
self.isChecked(!self.isChecked());
//still doesn't work with the set timeout
$('#thisFails').attr('checked', true);
console.log('e',e.isChecked());
}, 0);
}
If you step through the execution of it, you will see that the when it gets to the setTimeout console.log('set now'); that the checkbox has reverted to what it was when you clicked it.
Setting the self.isChecked then updates the observable and the computed is accessed and the checkbox display is updated.
Now the reason that works is quite a bit beyond my knowledge of browsers and their execution paths, but I think the setTimeout with the zero timeout value effectively adds this bit of code to run immediately after the current (click in this case) function, this link goes into some detail:
Why is setTimeout(fn, 0) sometimes useful?
I can't work out why setting checked attribute via your original jquery doesn't work when tried in that timeout function though.
I hope this actually helps you somehow!
I detect unsaved data within my form in order to warn the user if he leaves the page. This works so far for all elements, except for the primefaces calendar element.
Currently my approach relates to this answer. But this does not work for the calendar element. I found out that the change listener does not work for programmatically set values in general. Probably this is the reason for my problem. So I tried to implement my own very simple "change listener", by registering a click and blur listener to all my calendar elements. In the click listener I temporarily save the current value of the calendar element. In the blur listener I compare the saved value with the current value. But this approach doesn't work, because I'm not able to get the changed value of the calendar element within the blur listener. I tried to get the value in several ways:
$(this).val();
this.value;
$(this).html();
$(this).attr('value');
But in each case I get the old value. So I think the "simple change listener"-approach doesn't work as well.
My primefaces version is 3.4.2.
JSF implementation (probably not relevant) is: Mojarra 2.1.6
I don't know anything about primefaces but it uses the jquery ui calendar. So you could use the onSelect option of it to detect a change.
$('.hasDatepicker').each(function() {
var onSelect = $(this).datepicker('option', 'onSelect');
$(this).datepicker('option', 'onSelect', function() {
// some change happend here, set your flag that the form is dirty e.g.
setConfirmUnload(true);
// we call the original onSelect to not break the primefaces ajax change event
if(onSelect) { return onSelect.apply(this, arguments); }
});
});
I have a page with a TabContainer control (from the Ajax Control Toolkit), and I toggle the visibility of some elements on the page depending on the currently-selected tab. I'd been doing this in an event handler for OnClientActiveTabChanged (which works fine), but I discovered that it leaves the page in the wrong state after a postback. I tried adding some code to the document.ready event handler to get the index, but when I do the following:
$(document).ready(function () {
var index = $('#<%= TabContainer1.ClientID %>').[0].control.get_activeTabIndex();
// Do some stuff with the index
});
...I get a null reference exception on the .control property. Is there a way to hook a client-side "ready" event for the TabContainer?
I’m not familiar with the event lifecycle with normal DOM elements (it seems like there ought to be a general onload event, but I don’t see one). If there isn’t an event that can be easily handled, it seemed like it might work to add an UpdatePanel with UpdateMode=Conditional and an AsyncPostBackTrigger that pointed to a hidden button with an onclick event handler that would get the active tab index – but that seems like a lot of moving pieces for something that I’d expect the DOM to expose already.
Too late to be helpful, but I've had the same issue and found a workaround.
Change you code
$(document).ready(function () {
var index = $('#<%= TabContainer1.ClientID %>').[0].control.get_activeTabIndex();
// Do some stuff with the index
});
to
function pageLoad() {
var index = $('#<%= TabContainer1.ClientID %>').[0].control.get_activeTabIndex();
// Do some stuff with the index
};
Explanation here:
http://encosia.com/document-ready-and-pageload-are-not-the-same/
Basically, jQuery's ready event is "a bit early" and the TabContainer is not initialized yet, whereas the client side ASP.Net's pageLoad is late enough and the TabContainer has been initialized by then.
Check this post to save tab selection during postbacks. It works for normal post backs by saving the active tab index in hidden variable. Though its written for a JQuery plugin in the posted link, concept should be the same i.e., persisting the index of the selected tab.
I have a page with a select list (ASP.NET MVC Page)
The select list and onchange event specified like this:
<%=Html.DropDownList("CompanyID", Model.CompanySelectList, "(select company)", new { #class = "data-entry-field", #onchange = "companySelectListChanged()" })%>
The companySelectListChanged function is getting called twice?
I am using the nifty code in this question to get the caller.
both times the caller is the onchange event, however if i look at the callers caller using:
arguments.callee.caller.caller
the first call returns some system code as the caller (i presume) and the second call returns undefined.
I am checking for undefined to only react once to onchange, but this doesnt seem ideal, whcy would onchange be called twice?
UPDATE:
ok, found the culprit! ...apart from me :-) but the issue of calling the companySelectListChanged function twice still stands.
The onchange event is set directly on the select as mentioned. This calls the companySelectListChanged function.
..notice the 'data-entry-field' class, now in a separate linked javascript file a change event on all fields with this class is bound to a function that changes the colour of the save button. This means there is two events on the onchange, but the companySelectListChanged is called twice?
The additional binding is set as follows:
$('.data-entry-field').bind('keypress keyup change', function (e) { highLightSaveButtons(); });
Assuming its possible to have 2 change events on the select list, its would assume that setting keypress & keyup events may be breaking something?
Any ideas?
ANOTHER UPDATE:
The select element looks fine and all works if I remove the additional 'change' binding. when the 'change' binding is added that event fires once and the hard-wired 'onchange' is fired twice.
If both events are bound via jQuery all works ok, it seems that mixing hard-wired onchange and jquery bound change events cannot be mixed? I guess this answers my question but seems like an issue with IE and binding these events with jquery.
I agree with your assessment. I've updated my small example at http://gutfullofbeer.net/onchange.html to include a jQuery handler in addition to the DOM 0 handler (the one set with the "onchange" attribute). It appears to be a jQuery bug, or at least a failure of jQuery to deal with the IE weirdness.
I logged jQuery ticket 6593.
I also encountered this issue when using IE8 and made some changes to fix this.
Instead of specifying the onchange event on the dropdownlist, I used the jquery.change() event.
Markup:
#Html.DropDownList("drpList", myList, new { #class = "myClass", id = "drpList" })
Script:
$(function () {
$('#drpList').change(function () {
.... your functionality
});
});
This works for me.. Hopes this help.