I call the following function before the add dialog is shown; after clicking [+] on the JqGrid.
MVC Controller grid configuration
ordersGrid.ClientSideEvents.BeforeAddDialogShown = "initAddDialog";
The function makes an Ajax call to create a new order record either with or without a linking id, dependent on whether an existing order was selected when the [+] button was clicked.
The purpose is to make available the id necessary to make another Ajax call to retrieve additional linked information from another service, and to pre-populate the new record with date/time information and (where applicable) common information from an existing record.
function initAddDialog() {
var newOrderId = 0;
var newOrderLinkId = 0;
var selRow = jQuery('#clientOrderGrid').jqGrid('getGridParam', 'selrow');
var selRowData = jQuery('#clientOrderGrid').jqGrid('getRowData', selRow);
Get linking ID from selected row (if any)
var curOrderLinkId = (selRowData.OrderLinkId == null) ? 0 : selRowData.OrderLinkId;
Ajax call to create new 'Holding' Order
$.ajax({
url: '/Order/ajaxNewOrder?OrderLinkId=' + curOrderLinkId,
success: function (newOrderResponse) {
arr = newOrderResponse.split("|");
newOrderId = arr[0];
newOrderLinkId = arr[1];
},
error: function () { alert("There was an error creating an Order record"); }
});
If I break the code here using Firebug in Firefox, I can see that the variables newOrderId and newOrderLinkId have been set correctly with the id's from the newly created record, and if I hit F8 the (already displayed) dialog is populated with these //values.
If I don't break the code the dialog is displayed, but displays the values with which the variables were initialised i.e. newOrderId = 0, newOrderLinkId = 0.
$('#' + 'OrderId').val(newOrderId);
$('#' + 'OrderLinkId').val(newOrderLinkId);
$('#' + 'Stock').val(stock);
$('#' + 'SettlesTs').val(settlesTs);
$('#' + 'ReceivedTs').val(dtThis);
$('#' + 'ReceivedHHmm').val(dtTime);
I've tried calling the same function after the add dialog is shown, but get the same results.
Any thoughts as to why this is, or is there a better way of achieving the same result?
Thanks
I have taken the native add/edit dialog out of the equation and am using my own 'add' form along with the native inline editing of JqGrid. I'm sure with more research it would have been possible to find a solution, but with deadlines looming I had to find a workaround.
Related
I've created a site for sharing some old family letters. I'm using two Select2 controls to let people filter who wrote the letters and who received them. It's all working nicely, but I'd like to have each select2 get filtered based on what was selected in the other one. That is, if a user chooses a particular letter writer, then I'd like the select2 of recipients to show only people that letter writer wrote to. I've actually got that part working, but updating the contents of the dropdown removes any selections it had, and I can't get updating the selections to work.
A few notes and then I'll show some code and try to clarify what I'm seeing and trying.
Both select2 controls are set up for multiple selection.
There's a MySQL database that contains the information about what letters exist, who they were written by, and who they were sent to.
The HTML for the two select2 dropdowns is quite similar. Here's the code for the "From" dropdown:
<select id="from" class="js-example-basic-multiple" multiple="multiple" style="width: 100%">
<?php
echo GetPeople('F');
?>
</select>
GetPeople is a PHP function that calls out to the database and then converts the returned data to a series of lines. The parameter indicates whether to get the "From" data or the "To" data. That parameter is also used as part of the ID for each item (because it turns out that if options with the same ID appear in different select2 controls, things go downhill fast).
function GetPeople ($type) {
$people = getdata("GetPeople", "'$type',''", "Couldn't retrieve list of people.<br/>");
$return = '';
while ($obj = mysqli_fetch_object($people)) {
$return .= "<option value='" . $obj->FullName . "' id='" . $type . $obj->iid . "'>" . $obj->FullName . "</option>";
}
return $return;
}
The OnChange event for each of the select2 controls calls a JS function called updatePeople, passing either 'F' or 'T' to indicate which select2 needs its contents updated. That is, the From dropdown passes 'T' and the To dropdown passes 'F'.
updatePeople uses AJAX to go out and collect the data, build a new set of lines and sub them into the specified control. That's all working as expected. But doing so leaves that control with no items selected. When I try to restore the selections the control had before the update, nothing happens. Here's the code for updatePeople, including my attempt to restore the selection:
function updatePeople(cwhich) {
var results = '';
var success = false;
//get the currently selected list for the other dropdown, too
//so we can use them to restore selections later
if (cwhich == "F") {
var chosen = getChoices('#to');
var selecteditems = getChoices('#from',true);
} else {
var chosen = getChoices('#from');
var selecteditems = getChoices('#to',true);
}
var filters = cwhich + "','" + chosen + "'";
var fnargs = "GetPeople|'" + filters;
$.ajax({
url: 'retrievedata.php',
type: "POST",
async: false,
data: {"functionname": "getpeople", "arguments": fnargs},
dataType: "JSON",
success: function (obj) {
if (cwhich=="F") {
var target = "#from";
} else {
var target = "#to";
}
if ((obj===null)) {
//clear dropdown
$(target).html('');
}
else {
//build the list of options
var options = '';
for (line = 0; line < obj.length; line++) {
options += "<option value='" + obj[line].FullName + "' id='" + cwhich + obj[line].iid + "'>" + obj[line].FullName + "</option>";
}
window.allowupdate = false;
$(target).html(options);
//turn list of selected items into array
var arrSelected = selecteditems.split(',');
$(target).val(arrSelected);
$(target).trigger('change');
window.allowupdate = true;
}
success = true;
},
error: function (textStatus, errorThrown) {
success = false;
$("#testresult").text('Error occurred: '.textStatus);
console.log('Error occurred: '.textStatus);
}
});
}
The window.allowupdate bit is because trying to change the selections for one dropdown would fire its OnChange, and that would change the first one. So I added a flag that's checked in the OnChange code, so that I can change one without changing the other.
What I'm looking for is how to set the modified select2 control to have the same items selected as before updating the contents of the control. TIA
Solved my own problem. There were two issues:
Setting the control’s HTML seemed to make it unhappy. I changed that code to first disconnect from select2, then update the HTML, then reconnect to select2:
$(target).select2('destroy');
$(target).html(options);
$(target).select2({placeholder: 'Anyone (or choose senders)'});
I didn’t have the right value to reset the selections. My code was retrieving the primary keys for the selected items, but I needed their actual contents, which I was able to retrieve by calling the control’s val function.
Edit formatter action button is placed to jqgrid column:
colModel: [{"fixed":true,"label":" change ","name":"_actions","width":($.browser.webkit == true? 37+15: 32+15)
,"align":"center","sortable":false,"formatter":"actions",
"formatoptions":{"keys":true,"delbutton":false,"onSuccess":function (jqXHR) {actionresponse = jqXHR;return true;}
,"afterSave":function (rowID) {
cancelEditing($('#grid'));afterRowSave(rowID,actionresponse);actionresponse=null; }
,"onEdit":function (rowID) {
if (typeof (lastSelectedRow) !== 'undefined' && rowID !== lastSelectedRow)
cancelEditing($('#grid'));
lastSelectedRow = rowID;
}
}}
New row is added to jqgrid in loadcomplete event
var newRowData = {};
var newRowId = '_empty' + $.jgrid.randId();
$('#grid').jqGrid('addRowData', newRowId, newRowData);
and its id is updated if save action button is clicked:
function aftersavefunc(rowID, response) {
restoreActionsIcons();
$('#grid').jqGrid('resetSelection');
var json = $.parseJSON(response.responseText);
$("#" + rowID).attr("id", json.Id);
lastSelectedRow = json.Id;
$("#grid").jqGrid('setSelection', lastSelectedRow);
}
After clicking save action button edit action button clicks are ignored. It is not possible to re-enter to edit mode after first editing.
How to fix this so that row can edited by edit button click again after saving ?
Update
I added $(this).focus() as suggested in Oleg answer and also wrapped id change into setTimeout as Oleg recommends in other great answer:
function aftersavefunc(rowID, response) {
restoreActionsIcons();
$(this).focus();
$('#grid').jqGrid('resetSelection');
var json = $.parseJSON(response.responseText);
setTimeout(function () {
$("#" + rowID).attr("id", json.Id);
lastSelectedRow = json.Id;
$("#grid").jqGrid('setSelection', lastSelectedRow);
}, 50);
}
Problem persists. The problem may related to row id change since:
It occurs only in last row (where id is changed after save). It does not occur for saved rows where responseText returns same id and row id is actually not changed.
It does not occur if cancel action button is pressed.
Maybe row id needs additional reset id addition to resetSelection or needs updated in somewhere other place also.
Update2
I added code form updated answer to errorfunc and used only english characters and numbers id ids. This allows to click multiple times but introduces additional issue:
extraparam is no more passed. If rowactions() calls are commented out, extraparam is passed with with rowactions calls extraparam is not passed.
I changed jqGrid source code and added alert to rowactions method:
alert( cm.formatoptions);
if (!$.fmatter.isUndefined(cm.formatoptions)) {
op = $.extend(op, cm.formatoptions);
}
In first clicks alert outputs 'Object'. In succeeding clicks to Save button it outputs undefined. So for unknown reason formatoptions is cleared.
Remarks to comment:
Absolute url in testcase is not used. Datasource is set to localarray.
I verified that testcase works in IE and FF without external url access.
For extraparam issue I can create new testcase.
Without image directory buttons are shown in cursor is moved over them.
Missing image directory still allows to reproduce the issue.
FormData function is defined in js file.
Since new issue occurs after adding rowactions() calls and does not occur if those calls are removed, this seems to be related to the code proposed in answer.
I suppose that the problem exist because one hide a button which has currently focus. Look at the code from the answer. If one remove the line $(this).focus(); // set focus somewhere one has the same problem as you describes. So I suggest that you just try to set somewhere, for example in restoreActionsIcons the focus to any the table element of the grid after hiding the button having currently the focus. I can't test this, but I hope it will help.
UPDATED: I examined your problem one more time and I hope I can suggest you a solution.
You problem can be divided on two sub-problems. The main your problem is the the changing of the id of the row. So it is not common problem which everybody has.
The problem is that "actions" formatter create onclick functions directly in the HTML code (see for example here):
ocl = "onclick=$.fn.fmatter.rowactions('"+rowid+"','"+opts.gid+"','edit',"+opts.pos+");..."
So the functions will contains the original rowid. To fix the problem you can modify the code fragment of your aftersavefunc inside of setTimeout from
$("#" + rowID).attr("id", json.Id);
lastSelectedRow = json.Id;
$("#grid").jqGrid('setSelection', lastSelectedRow);
to something like the following:
var $tr = $("#" + rowID),
$divEdit = $tr.find("div.ui-inline-edit"),
$divDel = $tr.find("div.ui-inline-del"),
$divSave = $tr.find("div.ui-inline-save"),
$divCancel = $tr.find("div.ui-inline-cancel");
$tr.attr("id", json.Id);
if ($divEdit.length > 0) {
$divEdit[0].onclick = function () {
$.fn.fmatter.rowactions(newId,'grid','edit',0);
};
}
if ($divDel.length > 0) {
$divDel[0].onclick = function () {
$.fn.fmatter.rowactions(newId,'grid','del',0);
};
}
if ($divSave.length > 0) {
$divSave[0].onclick = function () {
$.fn.fmatter.rowactions(newId,'grid','save',0);
};
}
if ($divCancel.length > 0) {
$divCancel[0].onclick = function () {
$.fn.fmatter.rowactions(newId,'grid','cancel',0);
};
}
lastSelectedRow = json.Id;
$("#grid").jqGrid('setSelection', lastSelectedRow);
The second problem is that you use special characters inside of ids. I found a bug in the $.fn.fmatter.rowactions which need be fixed to support special characters in ids. The problem is that in the line 407 of jquery.fmatter.js the original rowid parameter rid will be changed:
rid = $.jgrid.jqID( rid )
and later everywhere will be used modified id. For example in the id is my.id the encoded version will be my\\.id. It's correct for the most places of the $.fn.fmatter.rowactions code (see here), but it' s incorrect as the rowid parameter of the editRow, saveRow, restoreRow, delGridRow, setSelection and editGridRow (see the lines 433-453). So the code must be fixed to use the original not escaped (not encoded) rid value with which the $.fn.fmatter.rowactions was called.
I think I will post tomorrow the corresponding bug report with the suggestions in the trirand forum.
UPDATED 2: The code $.fn.fmatter.rowactions(newId,'grid','edit',0); which I wrote above is just an example. I took it from the test demo which you send me. You should of course modify the code for your purpose. How you can see for example from the line the second parameter of the $.fn.fmatter.rowactions in the id of the grid which you use: 'grid', 'list' of something like myGrid[0].id. The last parameter should be the index of the column having formatter:'actions' in the colModel. You can use getColumnIndexByName function from the answer on your old question to get the index by column name.
My program does an ajax call when the user clicks on a radio button. Upon success, the background color of the table cell containing the radio button is changed to let the user know their selection has been posted to the database.
The problem is sometimes the background doesn't change. I'm trapping for errors, so I don't think it's because of an error. I'm wondering if the user is outpacing the success callback.
var setup = {};
setup.url = 'Gateway.cfc';
setup.type= 'POST'
setup.dataType='json';
$.ajaxSetup(setup);
var settings = {};
settings.data = {};
settings.data.method = 'Save';
settings.data.AssignmentID = $('input[name=AssignmentID]').val();
settings.error = function(xhr, ajaxOptions, thrownError) {
$('#msgErr').text(thrownError);
};
settings.success = function(result) {
$('#msg').empty();
$('#msgErr').empty();
if (result.RTN) { // uppercase RTN
$('#' + settings.data.AnswerID).addClass('answer');
} else {
$('#' + settings.data.AnswerID).next().append('<span class="err"> ' + result.MSG + '</span>');
}
}
$('input').filter(':radio').change(function() {
var myName = $(this).attr('name');
$('input[name=' + myName + ']').closest('td').removeClass('answer');
settings.data.AnswerID = $(this).val();
$.ajax(settings);
});
There is a delay between your Ajax post to the server and the ui element update on your screen. I do not know which Ajax library you are using, but you could plug into the Ajax framework and display a floating div element that covers the whole screen. This div could have other elements like an image or other divs, spans, p tags, etc. This is also called a dialog in some libraries.
I would recommend trying to find the before_Ajax_send and after_Ajax_receive functions in your Ajax library and attaching your functions to these events. The before_send function should display the floating div and the after_receive should close the div.
Hope this helps.
Gonna post this as an answer, on the off-chance that it does the trick :)
$('input').filter(':radio').change(function() {
$(this).closest('td').removeClass('answer');
var mySettings = $.extend(true, {data:{AnswerID: $(this).val()}}, settings);
$.ajax(mySettings);
});
This will make sure there are no race conditions with your settings if calls are made in quick succession.
I am building this slideshow, hereby a temp URL:
http://ferdy.dnsalias.com/apps/jungledragon/html/tag/96/homepage/slideshow/mostcomments
There are multiple ways to navigate, clicking the big image goes to the next image, clicking the arrows go to the next or previous image, and you can use your keyboard arrows as well. All of these events call a method loadImage (in slideshow.js).
The image loading is fine, however at the end of that routine I'm making a remote Ajax call using $.get. The purpose of this call is to count the view of that image. Here is the pseudo, snipped:
function loadImage(id,url) {
// general image loading routine
// enable loader indicator
$("#loading").show();
var imagePreloader = new Image();
imagePreloader.src = url;
loading = true;
$(imagePreloader).imagesLoaded(function() {
// load completed, hide the loading indicator
$("#loading").hide();
// set the image src, this effectively shows the image
var img = $("#bigimage img");
img.attr({ src: url, id: id });
imageStartTime = new Date().getTime();
// reset the image dimensions based upon its orientation
var wide = imagePreloader.width >= imagePreloader.height;
if (wide) {
img.addClass('wide');
img.removeClass('high');
img.removeAttr('height');
} else {
img.addClass('high');
img.removeClass('wide');
img.removeAttr('width');
}
// update thumb status
$(".photos li.active").removeClass('active');
$("#li-" + id).addClass('active');
// get the title and other attributes from the active thumb and set it on the big image
var imgTitle = $("#li-" + id + " a").attr('title');
var userID = $("#li-" + id + " a").attr('data-user_id');
var userName = $("#li-" + id + " a").attr('data-user_name');
$(".caption").fadeOut(400,function(){
$(".caption h1").html('' + imgTitle + '');
$(".caption small").html('Uploaded by ' + userName + '');
$(".caption").fadeIn();
});
// update counter
$(".counter").fadeOut(400,function() { $(".counter").text(parseInt($('.photos li.active .photo').attr('rel'))+1).fadeIn(); });
// call image view recording function
$.get(basepath + "image/" + id + "/record/human");
// loading routine completed
loading = false;
}
There is a lot of stuff in there that is not relevant. At the end you can see I am doing the $.get call. The problem is that it is triggered in very strange ways. The first time I navigate to a tumb, it is called once. The next time it is triggered twice. After that, it is triggered 2 or 3 times per navigation action, usually 3.
I figured it must be that my events return multiple elements and therefore call the loadimage routine multiple times. So I placed log statements in both the events and the loadimage routine. It turns out loadimage is called correctly, only once per click.
This means that it seems that the $.get is doing this within the context of a single call. I'm stunned.
Your problem may be:.imagesLoaded is a jQuery plug in that runs through all images on the page. If you want to attach a load event to the imagePreloader only, use
$(imagePreloader).load(function() {
...
}
Otherwise, please provide the code where you call the loadImage() function.
Update:
when clicking on a thumb That is the problem. $(".photos li a").live('click',... should only be called once on page load. Adding a click handler every time a thumb is clicked will not remove the previous handlers.
Another option is to change the code to $(".photos li a").unbind('click').live('click', ... which will remove the previously registered click handlers.
I have replaced the traditional select/option form elements with a nifty little popup window when a triggering image is clicked. The page is for accounting purposes and so multiple line items are to be expected. I've written the javascript that will dynamically generate new line item select/option elements. When the page loads, the initial set of choices loads and the user can click on them, get a pop up with some choices, choose one and then the box closes. The move to the next choice and so on and so forth. I've added livequery to my code for those dynamic elements. However... the livequery("click"...) seems to fire no matter where the user clicks on the page. Very frustrating.
I've read on here how great "live()" is in jQuery 1.3, but I am not able to upgrade fully to jquery 1.3 because a custom JS file depends on 1.2, so using live() is out of the question, however I have invoked the livequery() plugin and I really need to understand if I'm using it correctly.
I will post partial code. There's just way too much to post all of it.
Basically, I'm searching for divs starting with "bubble" and then a number afterwards. Then run the event on each them. Only bubble1 is static, 2 and up are dynamic. Am I missing the whole usage of livequery?
>$jb('div[id^="bubble"]').each(function () {
> var divid = $jb('div[id^="bubble"]').filter(":first").attr("id");
>var pref = "bubble";
>var i = divid.substring((pref.length));
>var trigger = $jb('#trigger' + i, this);
>var popup = $jb('#pop'+ i, this).css('opacity', 0);
>var selectedoption = $jb('selectedOption' + i, this);
>var selectedtext = $jb('selectedOptionText' + i, this);
>$jb([trigger.get(0), popup.get(0)]).livequery("click",
> function () {
>//alert(i);
// code removed for brevity (just the contents of the popups)
>});
Live works by using event delegation. A click event is attached to the body, and anytime something is clicked the selector is tested against the target. If it passes the selector test it calls the function (thus simulating a click event).
You probably want something like this:
$('div[id^="bubble"]').livequery("click", function() {
var divId = $(this).attr("id");
var i = divId.substring("bubble".length);
var trigger = $("#trigger" + i, this);
var popup = $("#pop" + i, this).css("opacity", 0);
// alert(i);
}