I wanna get the value of some checkboxes that is in my page, but the problem it is that this checkboxes are created by ajax, and how the javascript is executed before the my ajax mount my checkboxes, the function that gets the value of this element is null.
The code is like this:
function salvar()
{
//here is the problem, because this inputs is not here,
//I have a ajax function that creates this inputs
var checkBoxes = $('input[name=checkbox]');
var idsTelas;
var i = 0;
$.each(checkBoxes, function()
{
if ($(this).attr('checked'))
{
idsTelas[i] = $(this).val();
i++;
}
});
}
I suspect you are going about this the wrong way:
I wanna get the value of some checkboxes that is in my page, but the problem it is that this checkboxes are created by ajax, and how the javascript is executed before the my ajax mount my checkboxes
If I understand you correctly, your Javascript is executing before the checkboxes are created/inserted into the page.
So - when the Javascript runs, you want to get the value of things that don't exist yet? That is quite simply not going to be possible, regardless of any clever tricks.
The only way to resolve this is to change the relative timings. You'll need to run your salvar() function after the checkboxes have been created by AJAX. Or alternatively, if you can't push back execution of the function for some reason, you'll need to bring the AJAX creation step forwards. Either way, there has to be a dependency between creating the checkboxes and then invoking salvar().
Related
I have a few rows of products a customer can choose to select. These rows have the class "product-line". On the click of a row, I want to show the user that it is (un)checked by toggling the "product-checked" class, and then send a POST message with the IDs all checked rows. To do this, I wrote this code:
<script>
$(".product-line").click(onProductLineClick);
function onProductLineClick(e) {
$(this).toggleClass("product-checked");
updatePrice();
}
function updatePrice() {
var ids = $(".product-checked")
.map(function() {
return $(this).data("id");
});
$.post("/Controller/Action", { 'productIds[]': ids });
alert("Hi!");
}
</script>
This piece of code causes the onProductLineClick to trigger indefinitely. The alert("Hi!") is never triggered. The POST is never sent.
When I remove the $.post, the click event is triggered only once and everything works fine. The toggleClass does its job, showing that the row was selected. When I do add the $.post, it all goes nuts and the moment the $.post is reached, it just fires the click event again, causing an infinite recursion.
The parameter 'e' in onProductLineClicked is never defined other than the very first time. Using e.preventDefault() causes an error on the second time it passes through there.
I'm stumped. I can't understand how or why a POST would cause the click to fire again, and remove the POST stops that from happening.
The error is with your map function. There are two map functions in jQuery.
$('element').map() versus jQuery.map()
As stated in the documentation, the first produces a new jQuery object containing the return values while the second translates all items in an array or object to new array of items.
Serialising the newly created object and passing it in an ajax request doesn't work because it has recursive references within it. [Thanks #ADyson]
FIDDLE
$(".product-line").click(onProductLineClick);
function onProductLineClick(e) {
$(this).toggleClass("product-checked");
updatePrice();
}
function updatePrice() {
var ids = jQuery.map($(".product-checked"), function(element, index) {
return $(element).data("id");
});
console.log(ids);
$.post("/Controller/Action", {
'productIds': ids
});
alert("Hi!");
}
This question is for using javascript in Caspio Cloud's platform, so a few things might look different than normal. But basically, I need to put an 'if' statement that references a variable 'above' a change function that runs some other code and I can't figure it out.
The problem is that in Caspio's form the Virtual9 field will 'change' on the form's load, and so it runs the change js. I need to not run the change js onload and also only if the variable 'item' is null.
<script>
var item = document.getElementsByName("InsertRecordItemID")[0].value;
<!-- right here I need an if (item !== null) to then run the change function but that if it's not null to not run the change function{ -->
$("[name='cbParamVirtual9']").change(function(){
var itid = document.getElementsByName("cbParamVirtual9")[0].value;
document.getElementsByName("InsertRecordItemID")[0].value = itid;
}
);
</script>
I've tested the variables using alert windows and they are gotten correctly. I just need to make sure the change function doesn't run if the variable 'item' is null.
EDIT: To clarify, when the form loads the ItemID field may or may not be null (based on parameters that pass over).
If it is not null then the Virtual9 field (where the user makes a selection) will be hidden- because the ItemID already had a value and needs to not change.
However, if ItemID is null then Virtual9 will be visible and the user can make a change, thereby updating the ItemID field. The user may update Virtual9 multiple times (looking for the right record) so it needs to keep updating ItemID (even after it is no longer null in this case).
That's why I think the best solution is to disable the code ONLY for onload, if possible. So that the code runs on change of Virtual9 but not onload.
I came across a peculiar issue when trying to make an ajax call and isolate the actions of the function to itself. Here is the code snippet
$(document).on('click', 'input.action', function(event) {
var self = this;
$.ajax({
url:'http://date.jsontest.com/',
method:'GET',
cache:false,
dataType:'json',
success:self.process,
error:function(){self.process(false);}
});
self.process = function(data) {
if (data) {
alert(data.time);
}
else {
alert("Operation Failed!");
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="container">
<input type="button" value="Get Time" class="action"/>
</div>
Let me briefly explain what I am trying to do, on click of the button, I wish to receive some data from the server and display the data. I am using a process function to process the received data. In case of an error I reuse the process function in a different way to display the error message. I simply use the self variable to contain all the elements within the parent function. I fully understand the following:
What I know
I do not have to use the self to contain the process function because another method will not have access to it
Because the process method in the snippet above is declared after the ajax call for the program as far as it is concerned the process function is undefined.
I clearly know how to fix it.
Experiment:
Click on the Get Time button
Wait for as long as you want but see no result, which is expected because of the process function is declared after the ajax call
Click on the Get Time button again
It works now! Some time (which is probably not your time :P) is displayed now!!
What I wish to know:
What just happened? why does it work the second time and everytime after? Remember this only works for ajax calls, if it were the case that the assignment is retained in the function after calling it once, then this should work in every situation but it does not. Here is an experiment to show that it does not work the same way when ajax calls are not used: Fiddle - Experiment
The Solution:
I am adding a sample solution based on #Felix Kling's answer below. In the Sample Solution, there are two buttons Get Time and Get Date. I've attached the parameter to retrieve time in case of Get Time and date in the case of Get Date to the object self and it is interesting that once I click on Get Time nothing happens just like before but if I click on either Get Time or Get Date the second time only time is displayed.
What just happened?
In a simplified way, this:
var process;
// First click
ajaxCall(process); // process is still undefined
process = function() { ... };
// second click
ajaxCall(process); // process is defined
process = function() { ... };
The assignment to self.process "persists" between events because self refers to the same element.
Here is an experiment to show that it does not work the same way when ajax calls are not used: ...
It doesn't work in your fiddle because of one big difference: you are trying to execute process immediately. But it doesn't exist, so an error is thrown. At this point the script terminates and won't execute the rest of the function, where the function definition takes place.
In your example here, the execution of process is delayed. The script "doesn't know" that there is no function to call until the response was received.
There is nothing "special" going on here. Yes, accepted that the first time it returns undefined as it is yet to be defined.
But, you're attaching the function to the same element input.action. So, the next time, when you click the button, this already has the process method attached to it, so gets called when clicked again.
Try adding one more button with same class and click each once. Now though you've clicked the first button, clicking the second button will still not create an alert as it has not yet had the process function attached to it.
Background
I've got asp.net webform with a grid, and when users update textboxes in that grid, the onchange event kicks off a WebMethod call and updates the rest of the changed row. Nothing is saved at that time -- we're just updating the UI.
To commit the changes, you click the save button.
This actually works reliably in almost every scenario. However, there is one very persistant one that it feels like I should be able to solve, but it's time to call in the specialists.
The Problem Scenario
I'm using jQuery to capture the enter key, and unfortunately that event fires first, causing the page to submit before the callback completes. The row is not updated correctly. Stale and bewildering data is saved.
Update
I don't think you can make the enter behavior depend on the callback, because you could save without changing a row. In that case, if you didn't change a row, it would never save.
Now if there was some way to inspect javascript's internal list of things to do, or maybe create my own and then manage it somehow, that would work. But that's some heavy lifting for something that should be easy. So unless an expert tells me otheriwse, I have to assume that's wrong.
Attempts
Right now I'm using the built-in jQuery events and I've got this elaborate setTimeout persisting the fact that a save was attempted, pausing long enough for the WebMethod to at least get called, and relying on the callback to do the submit. But it turns out javascript ansychrony doesn't work the way I hoped, and the onchange event doesn't even fire until that chunk of code completes. That was surprising.
I was thinking I could use my own little object to queue up these events in the right order and find a clever way to trigger that, etc.
This all seems like the wrong direction. Surely this is insane overkill, this is a common problem and I'm overlooking a simple solution because I don't work in javascript 24/7.
Right?
Code
Here's what I've got right this minute. This obviously doesn't work -- I was trying to take advantage of the async nature of jquery, but all of this apparently has to conclude before the row's onchange event event fires:
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) {
handleEnter();
return false; //apparently I should be using e.preventDefault() here.
}
});
function handleEnter() {
setTimeout(function () {
if (recalculatingRow) { //recalculatingRow is a bit managed by the onchange code.
alert('recalculating...');
return true; //recur
}
//$('input[id$="ButtonSave"]').click();
alert('no longer recalculating. click!');
return false;
}, 1000);
}
And then a typical row looks like this. Note that I'm not using jquery to bind this:
<input name="ctl00$MainContent$GridOrderItems$ctl02$TextOrderItemDose" type="text" value="200.00" maxlength="7" id="ctl00_MainContent_GridOrderItems_ctl02_TextOrderItemDose" onchange="recalculateOrderItemRow(this);" style="width:50px;" />
I could post the code for recalculateOrderItemRow, but it's really long and right now the problem is that it doens't fire until the after keypress event concludes.
Update Dos
According to Nick Fitzgerald (and man is that a cool article) the use of setTimeout should cause this to become async. Digging further into interactions between setTimeout and jQuery, as well as interactions between normal javascript events and jQuery events.
Preventing ENTER shouldn't be causing you so much trouble! Make sure you have something like this on your code:
$(document).on('keydown', 'input', function(e) {
if(e.keyCode == 13) {
e.preventDefault();
}
});
UPDATE
It looks like you do want to save on ENTER, but only after the UI is updated on change. That is possible. You could use a flag a Matthew Blancarte suggested above, trigger save from the change callback, and get rid of the setTimeout.
But I wouldn't recommend that. You are better off relying solely on the save button for saving. If you don't, your users will have to wait for two async operations to complete before saving is finished. So you'd have to block the UI, or keep track of all async operations, aborting some as needed. I think it's not worthy, ENTER becomes less intuitive for the users if saving takes too long.
The hideous mass of workarounds below, which effectively took me all day today and half of yesterday to write, seems to solve every permutation.
The amusing thing is that enter itself doesn't trigger onchange, if you call e.preventDefault(). Why would it? The change doesn't actually happen until the default behavior of clicking the save button occurs.
Very little else about this is amusing.
//Used in handleEnter and GridOrderItems.js to handle a deferred an attempt to save by hitting enter (see handleEnter).
var isSaving = false;
var saveOnID = '';
//When one of the fields that trigger WebMethods get focus, we put the value in here
//so we can determine whether the field is dirty in handleEnter.
var originalVal = 0;
//These fields trigger callbacks. On focus, we need to save their state so we can
//determine if they're dirty in handleEnter().
$('[id$=TextOrderItemDose], [id$=TextOrderItemUnits]').live("focus", function() {
originalVal = this.value;
});
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) { //enter pressed.
e.preventDefault();
handleEnter();
}
});
//Problem:
//In the products grid, TextOrderItemDose and TextOrderItemUnits both have js in their onchange events
//that trigger webmethod calls and use the results to update the row. Prsssing enter is supposed to
//save the form, but if you do it right after changing one of those text fields, the row doesn't always
//get updated due to the async nature of js's events. That leads to stale data being saved.
//Solution:
//First we capture Enter and prevent its default behaviors. From there, we check to see if one of our
//special boxes has focus. If so, we do some contortions to figure out if it's dirty, and use isSaving
//and saveOnID to defer the save operation until the callback returns.
//Otherwise, we save as normal.
function handleEnter() {
var focusedElement = $("[id$=TextOrderItemDose]:focus, [id$=TextOrderItemUnits]:focus")
//did we press enter with a field that triggers a callback selected?
if (isCallbackElement(focusedElement) && isElementDirty(focusedElement)) {
//Set details so that the callback can know that we're saving.
isSaving = true;
saveOnID = focusedElement.attr('id');
//Trigger blur to cause the callback, if there was a change. Then bring the focus right back.
focusedElement.trigger("change");
focusedElement.focus();
} else {
forceSave();
}
}
function isCallbackElement(element) {
return (element.length == 1);
}
function isElementDirty(element) {
if (element.length != 1)
return false;
return (element.val() != originalVal);
}
function forceSave() {
isSaving = false;
saveOnID = '';
$('input[id$="ButtonSave"]').click();
}
This gets called in the change event for the textboxes:
function recalculateOrderItemRow(textbox) {
//I'm hiding a lot of code that gathers and validates form data. There is a ton and it's not interesting.
//Call the WebMethod on the server to calculate the row. This will trigger a callback when complete.
PageMethods.RecalculateOrderItemRow($(textbox).attr('id'),
orderItemDose,
ProductItemSize,
orderItemUnits,
orderItemUnitPrice,
onRecalculateOrderItemRowComplete);
}
And then, at the end of the WebMethod callback code we pull the updated form values out, put the caret where it needs to be using jquery.caret, and check to see if we need to force a save:
function onRecalculateOrderItemRowComplete(result) {
var sender, row;
sender = $('input[id="' + result.Sender + '"]');
row = $(sender).closest('tr');
row.find('input[id$="TextOrderItemDose"]').val(result.Dose);
row.find('input[id$="TextOrderItemUnits"]').val(result.Units);
row.find('span[id$="SpanTotalPrice"]').html(formatCurrency(result.TotalPrice));
calculateGrandTotalPrice();
$(document.activeElement).select();
if (isSaving && saveOnID == result.Sender) {
forceSave();
}
}
result.Sender is the ID of the calling control, which I stuffed into the WebMethod call and then returned. saveOnID may not be perfect, and it might actually be even better to maintain a counter of active/uncallback-ed WebMethod calls to be totally sure that everything wraps up before save. Whew.
Can you post your javascript? Sounds like you're on the right track. I would change my OnChange events to increment a variable before making the AJAX call. I'll call the variable inProcess and initialize it to zero. When the AJAX call comes back, I would update the inProcess to the current value minus one. On the Enter key event, I would check to that inProcess equals zero. If not, you could either warn the user or set a timeout to try again in a bit.
You could unbind the Enter key capture while you are in the onChange event, then rebind it at the end of the callback function. If you post some code, I could give a more specific answer.
It sounds like you shouldn't be calling the WebMethod asynchronously. Call it synchronously, and on success, save your data.
I have a problem with my code, some code does not work when I call recursive call of same function inside it. It has to be something with myGalleria = Galleria.get(0);, but I have no idea how to make it all work.
Document Ready (just to show when I call function for the first time, everything works fine for first time)
$(document).ready(function(){
$.getJSON('getImages.php', {
cat: "123"
}, function(imgData){
createGallery(imgData);
});
});
Now function itself, note that when I click on .galleria-menuButtons span that calls same function nothing is working, well galleria itself is creating, but nothing else.
function createGallery(imgData){
$("#gallery").galleria({
image_margin: 30,
clicknext: true,
transition: "fade",
dataSource: imgData
});
myGalleria = Galleria.get(0); // I don't think this works after recursive call
// Adding menu and menu buttons
myGalleria.addElement("menu").appendChild("container", "menu");
myGalleria.addElement("menuButtons").appendChild("menu", "menuButtons");
$.ajax({
url: "menuButtons.php",
success: function(data){
myGalleria.$("menuButtons").html(data);
}
});
// Menu button click events
$('.galleria-menuButtons span').live('click', function(){
alert(this.id);
// Getting jSon data
$.getJSON('getImages.php', {
cat: this.id
}, function(imgData) {
alert(imgData);
createGallery(imgData); // <- Recursive call
});
});
}
I have similar function on window.resize and it also does not work after recursive call.
$(window).resize(function(){
$(".galleria-container").css("width", $(window).width());
$(".galleria-container").css("height", $(window).height());
galleriaRescale = Galleria.get(0);
galleriaRescale.rescale(); // <- this is not working either
//sizeBG();
});
FYI - this isn't actually recursion in the traditional sense because you're calling createGallery from a click handler which launches a JSON request which then calls createGallery when that succeeds, both of which will occur after the previous call to createGallery finishes.
But you do have surviving function closures which could be confusing things or causing problems. One guess is that you may want to make sure that things you expect to be local variables (like myGalleria have a var in front of them so they really are local variables and not variables that might be scoped to a higher level and be influenced by a previous incarnation of this call or be influencing an earlier call that hasn't yet completed.
var myGalleria = Galleria.get(0);
Then, assuming imgData is some sort of data structure like an array or object, you have to make sure that there's either only one global version of that data structure that never changes or that each call of createGallery has the appropriate separate copy of that data structure. If it's getting changed along the way, then subsequent calls to createGallery may not be getting the data they want. If it's a read-only data structure (you don't change it), then you're probably OK on that one.
OK, let's talk through the pseudo code for what this does.
On page ready, you get some JSON image data.
When that succeeds, you call createGallery with that image data.
The createGallery call does some sort of operation in the DOM (perhaps an animation)
It then calls: myGalleria = Galleria.get(0); Because there is no var in front of myGalleria, this is a global variable declaration (bad news for recursion and closures)
You then use the myGalleria data structure to make some changes to the DOM (adding menus and menu items).
You then add a .live click handler on a pretty generic set of CSS classes (it's possible you have added this click handler more than once here).
You then fetch some JSON image data again.
When that image data is fetched, you start the whole process over again by called createGallery.
Summary
The two potential problems I see are that myGalleria is not a local variable and probably should be and you may be adding duplicate click handlers.
If neither of these fully solve the issue, then we probably need more information about what Galleria.get(0) is doing.
FYI, the resize clickHandler looks like it may have the same issue with not using var to make your variable declaration a local variable.
Round 2
OK, here are some more observations.
When you add the menu and menu buttons with this block of code, you aren't providing any unique identifiers to either the addElement or appendChild functions (you're providing "menu" and "menuButtons" to both). As such, I don't know how you can uniquely hook up to them in the subsequent click event. As far as your code looks, all the menu items look identical and none have unique state. I don't know the Galleria code, but I assume somebody has to make unique identifiers for these new items so that you can uniquely identify them in your subsequent click handler.
// Adding menu and menu buttons
myGalleria.addElement("menu").appendChild("container", "menu");
myGalleria.addElement("menuButtons").appendChild("menu", "menuButtons");
When you set up a click handler to presumably handle the clicks for just these menu items, you are using the exact same CSS selector every time so there's no way that this click handler is going to be uniquely assigned to just the newly create menu items (which is what I assume you want). I don't know the Galleria code, but I assume that you should create some sort of unique ID that you pass into addElement and appendChild for the newly created menu items and then reference that unique identifier when you install the click handler. Likewise, this function needs to uniquely target just the menu buttons you created by using unique identifiers myGalleria.$("menuButtons").html(data);
Lastly, I'd suggest you change the name of one of your variables just to avoid confusion. In your click handler, change the three occurrences of imgData to just data so there can be no confusion about closures and the value of imgData.
Round 3
Ultimately one of the fixes was this (embedded in the comments):
I think it might work if you just only install the .live click handler once outside the createGallery function rather than call it each time. Since it's .live it will automatically work for all future buttons you create so you should only call it once. I'd suggest putting it in the $(document).ready function block.