I am using a fancybox plugin on one of my ASP MVC views to display error messages via an Ajax call. However, it appears to be caching the results. Below is the Linq query I use to retrieve errors from the table
public JsonResult GetErrors(string term)
{
try
{
int id = int.Parse(term);
var errors = (from e in db.TransmissionHistory
where (e.TransmissionTable == TABLE) &&
(e.TranTableId == id) &&
(e.ReplyResult == RESULT)
orderby e.TransmittedOn descending
select e).FirstOrDefault();
if (errors == null)
{
return Json("Search returned no results", JsonRequestBehavior.AllowGet);
}
List<string> errs = new List<string>();
errs = errors.Errors.Split(',').ToList();
return Json(errs, JsonRequestBehavior.AllowGet);
}
catch (Exception)
{
return Json("There was an error processing your request, please try again", JsonRequestBehavior.AllowGet);
}
}
If I set a breakpoint at the first line: int id = int.Parse(term);
this code will only execute the first time I click on the pic that initiates the fancybox/Ajax call. Consequently, this winds up being the only information that is displayed in the fancybox modal.
The linq query is supposed to grab the most recent error message (as determined by the date field), however if a new error message occurs after you initiate the fancybox/ajax call you will not see it. Instead the results of the previous query are simply displayed again. Also, to reiterate, in this instance (initiating the call a second time) the breakpoint does not fire.
I'm not very familiar with this plugin, so I would assume this means that I am somehow caching the results of the call. However, I couldn't find much on the net detailing fancybox's caching properties and/or how to alter them. For good measure, here is the fancybox jquery:
$('.checkErrors').click(function () {
var $tr = $(this).closest('tr');
var firstName = $tr.find('td:nth-child(2)').text();
var lastName = $tr.find('td:nth-child(3)').text();
$.ajax({
type: 'GET',
url: '#Url.Action("GetErrors","AgentTransmission")',
data: { term: $(this).attr('id') },
dataType: 'json',
success: function (data) {
var display = "<center><h2>" + firstName + " " + lastName + "</h2></center><ul>";
if (data == "Search returned no results" || data == "There was an error processing your request, please try again") {
display += "<li>" + data + "</li>";
} else {
$.each(data, function (key, value) {
display += "<li>" + value + "</li>";
});
}
display += "</ul>";
$.fancybox(display, {
// fancybox API options
fitToView: false,
autoScale: true,
autoDimension: true,
closeClick: true,
openEffect: 'fade',
closeEffect: 'fade',
closeBtn: true,
openSpeed: 'fast',
closeSpeed: 'fast'
});
},
error: function (data) {
$.fancybox("There was an error processing your request. We apologize for the inconvenience!", {
// fancybox API options
fitToView: false,
autoScale: true,
autoDimension: true,
closeClick: true,
openEffect: 'fade',
closeEffect: 'fade',
closeBtn: true,
openSpeed: 'fast',
closeSpeed: 'fast'
});
}
});
});
This was a result of the caching property not being set on the Ajax call, not fancybox. Simply adding cache: false to the ajax call fixed the issue.
Related
I recently upgraded from the original Tony Tomov's jqGrid v4.5.4 to Oleg's free-jqGrid 4.15.6.
When using 4.5.4, the code below worked perfect, but in 4.15.6, it does not. The run-time error is produced by the two calls to expandNode and expandRow located in the forceNodeOpen() function. The error given:
TypeError: rc1 is undefined
The forceNodeOpen() function is used to force all ancestor nodes of the current treegrid node to show as expanded. Much like a table of contents...if we load an initial topic node, we want the whole topic hierarchy to be expanded:
// force a node (if expandable) to stay expanded
function forceNodeOpen(rowid)
{
var record = $("#tree").jqGrid('getRowData', rowid);
var div = $('tr#'+rowid).find('div.ui-icon.treeclick');
div.removeClass('ui-icon-triangle-1-e tree-plus').addClass('ui-icon-triangle-1-s tree-minus');
**$('#tree').jqGrid('expandNode', record);**
**$('#tree').jqGrid('expandRow', record);**
// get all ancestoral parents and expand them
// *NOTE*: the getAncestorNodes function of grid was not usable for
// some reason as the same code below just would not work
// with the return array from getAncestorNodes
var parent = $("#tree").jqGrid('getNodeParent', record);
while(parent)
{
forceNodeOpen(parent['id']);
parent = $("#tree").jqGrid('getNodeParent', parent);
}
}
// using topic url, get the tree row id
function getTopicID(topic)
{
var nodes = $('#tree').jqGrid('getRowData');
var rowid = 1;
$.each(nodes, function(e,i)
{
var url = $(this).attr('url');
if(url == topic)
{
rowid = $(this).attr('id');
return false;
}
});
return rowid;
}
// post request to help server via ajax
function loadTopic(topic)
{
// no need to load again
if(loadedtopic == topic) { return false; }
// select the topic node
var rowid = getTopicID(topic);
loading = true;
$('#tree').jqGrid('setSelection', rowid);
forceNodeOpen(rowid);
loading = false;
// wipe content
$('h1#help_content_topic span:first').html('Loading...');
$('div#help_content').html('');
// block UI for ajax posting
blockInterface();
// request help content
$.ajax(
{
type: 'POST',
url: '/index.php',
data: { 'isajax': 1, 'topic': topic },
success: function(data)
{
$.unblockUI();
$('h1#help_content_topic span:first').html(data['topic']);
$('div#help_content').html(data['content']);
return false;
}
});
// save current topic to prevent loading same topic again
loadedtopic = topic;
}
// table of contents
$('#tree').jqGrid({
url: "topics.php",
datatype: "xml",
autowidth: true,
caption: "Help Topics",
colNames: ["id","","url"],
colModel: [
{name: "id",width:1,hidden:true, key:true},
{name: "topic", width:150, resizable: false, sortable:false},
{name: "url",width:1,hidden:true}
],
ExpandColClick: true,
ExpandColumn: 'topic',
gridview: false,
height: 'auto',
hidegrid: false,
pager: false,
rowNum: 200,
treeGrid: true,
treeIcons: {leaf:'ui-icon-document-b'},
// auto-select topic node
gridComplete: function()
{
// save current topic to prevent loading same topic again
loadedtopic = '<? echo($topic) ?>';
var rowid = getTopicID('<? echo($topic) ?>');
$('#tree').jqGrid('setSelection', rowid);
forceNodeOpen(rowid);
$.unblockUI();
},
// clear initial loading
loadComplete: function()
{
loading = false;
},
onSelectRow: function(rowid)
{
// ignore initial page loads
if(loading) { return false; }
// load the selected topic
var topic = $("#tree").jqGrid('getCell',rowid,'url');
loadTopic(topic);
}
});
The forceNodeOpen(rowid) is invoked from the loadTopic() function, which is called inside the onSelectRow() event of the treegrid.
Not sure what 4.5.4 did that allowed this code to work but 4.15.6 finds it to be an error. The offending line in 4.15.6.src.js:
expandNode: function (rc) {
...
if (p.treedatatype !== "local" && !base.isNodeLoaded.call($($t), p.data[p._index[id]]) && !$t.grid.hDiv.loading) {
// set the value which will be used during processing of the server response
// in readInput
p.treeANode = rc1.rowIndex;
p.datatype = p.treedatatype;
...});
I have only included a few lines from the above core function. It's the p.treeANode = rc1.rowIndex that throws the error.
I have to be missing something but do not know what. Hoping somebody can tell me what to do. If I remark out the two expandNode and expandRow treegrid function calls in forceNodeOpen() function, the system does not error out and the desired topic loads. But the hierarchy is not expanded as desired.
START EDIT 1
The server-side code that returns the topic nodes:
echo("<?xml version='1.0' encoding='UTF-8'?>\n");
require('db.php');
echo("<rows>\n");
echo("<page>1</page>\n");
echo("<total>1</total>\n");
echo("<records>1</records>\n");
$sql = "SELECT node.id, node.parentid, node.topic, node.url, node.lft, node.rgt, (COUNT(node.parentid) - 1) AS depth
FROM helptopics AS node, helptopics AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.url ORDER BY node.lft";
$stmt = AMDB::selectStatement($sql);
while($data = $stmt->fetch(PDO::FETCH_ASSOC))
{
$id = $data['id'];
$pid = $data['parentid'];
$topic = $data['topic'];
$url = $data['url'];
$lft = $data['lft'];
$rgt = $data['rgt'];
$leaf = $rgt - $lft == 1 ? 'true' : 'false';
$exp = 'false';
$dep = $data['depth'];
echo("<row><cell>{$id}</cell><cell>{$topic}</cell><cell>{$url}</cell><cell>{$dep}</cell><cell>{$lft}</cell><cell>{$rgt}</cell><cell>{$leaf}</cell><cell>{$exp}</cell></row>\n");
}
echo("</rows>\n");
exit();
END EDIT 1
I see that you use
var record = $("#tree").jqGrid('getRowData', rowid);
to get node data of TreeGrid. It wasn't good, but it worked in old jqGrid because it saved internal jqGrid data twice: once as local data and once more time in hidden cells of the grid.
You should use getLocalRow instead of getRowData:
var record = $("#tree").jqGrid('getLocalRow', rowid);
The code will work on both old jqGrid and free jqGrid. Additionally, getLocalRow has always better performance comparing with getRowData even in jqGrid 4.5.4.
I'm using autocomplete with materialize and i'm retrieving the data with ajax call, it works fine but when i want to call ajax only after entering caracters(using onkeyup event), the drop down list will not be showing correctly !!!!
Before i forget please help me to show a "NOT FOUND" in the drop down list if no data founded (because my else condition doesn't work). here is my code and thanks a lot in advance :
$(document).ready(function() {
var contents = $('#autocomplete-input')[0];
contents.onkeyup = function (e) {
$.ajax({
type: 'GET',
url: Routing.generate('crm_search_lead', {"search":
$(this).val()}),
success: function (response) {
var contacts = {};
if (true === response.success) {
var result = response.result;
for (var i = 0; i < result.length; i++) {
var lastName = result[i].lastName ?
result[i].lastName : '';
var firstName = result[i].firstName ?
result[i].firstName : '';
contacts[lastName + " " + firstName] = null;
}
$('input.autocomplete').autocomplete({
data: contacts,
minLength: 2,
});
} else {
$('input.autocomplete').autocomplete({
data: {
"NOT FOUND": null
}
});
}
}
});
}
});
Hi people :) i resolve it by changing onkeyup() with focus() and it's totally logical because with onkeyup() the droplist will appear and disappear very quickly on every key entered.
I have an MVC controller that has several Methods on it. One to show the View, 6 that are for jquery ajax methods. The View shows up correctly and here is the simple code
public ActionResult Queues()
{
return View();
}
On that view there are 2 datatable.net grids. That grid gets populated with a ajax call to this
[HttpGet]
public async Task<JsonResult> QueueOne()
{
try
{
....
var results = await GetData(queryString, authUser.AccessToken).ConfigureAwait(false);
var jsonObj = JsonConvert.DeserializeObject<DataTableWrapper<QueueItemForRead>>(results);
return Json(jsonObj, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Logger.Error(ex.Message, ex);
}
return Json("Error occured please try again");
}
which populates the grid correctly.
I have other functions on the page that call another endpoint on the page
public async Task<JsonResult> ItemComplete(Guid QueueId, long version)
{
try
{
...
var results =
await
PutData(queryString, JsonConvert.SerializeObject(itemCompleted), authUser.AccessToken)
.ConfigureAwait(false);
var jsonObj = JsonConvert.DeserializeObject<NewItemCommandResult>(results);
return Json(jsonObj, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Logger.Error(ex.Message, ex);
}
return Json("Error occured please try again");
}
and here is the JS that calls the above endpoint
$.ajax({
url: 'http://localhost:18171/Clients/CurrentActivity/ItemComplete' + "?QueueId=" + data + "&version=" + version,
type: 'PUT',
//contentType: 'application/json',
//dataType: 'json',
success: function (result) {
if (result.Result === 2) {
showSuccessNotification(name +
" has been Delivered to table.",
"Food Delivered");
}
//else {
//}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
//Process error actions
console.log(XMLHttpRequest.status + ' ' +
XMLHttpRequest.statusText);
$(buttonName).hide();
},
beforeSend: function () {
// Code to display spinner
$(buttonName).hide();
$(completedAjax).show();
},
complete: function () {
// Code to hide spinner.
$(completedAjax).hide();
}
});
but everytime this function is run all I get a 404 error.
Sorry it was bad cut and paste job, the URL actually has signle quotes around it. and i get the base Url this way
var QueueUrl = '#Url.Action("QueueOne","CurrentActivity")';
so when it renders the actual url is '/Clients/CurrentActivity/QueueOne'
your url doesn't contain " " Or ' ' so it isn't considered as string it should be like
url: "/Clients/CurrentActivity/ItemComplete?QueueId=" + data + "&version=" + version,
Don't Use Your Local Domain IN the Url This Will Cause Problem In production Version if you forget to change it
you can also use Url Helper To Make valid Url Like
url: "#Url.Action("ItemComplete","CurrentActivity",new{area='Clients'})"+"'QueueId=' + data + "&version=" + version,
Right now I have a collection that fetches value, and after that every view attached to the reset event get rendered again
the problem is that I also have to issue another query to fetch the total number of records retrieved, and only after that ajax call is completed the reset event should be triggered
is more clear with a bit of code:
fetch: function() {
options = { data: this.getParams() };
this.fetch_total();
return Backbone.Collection.prototype.fetch.call(this, options);
},
fetch_total: function() {
var that = this;
var options = {
url: this.url + '/count',
data: this.getParams(),
contentType: 'application/json',
success: function(resp, status, xhr) {
that.total = parseInt(resp);
return true;
}
};
return $.ajax(options);
}
as you can see, I have to issue a get to localhost/myentity/count to get the count of entities...
The thing is I need the collection.total varaible to be updated before refreshing the views, that means I need both request, the GET to localhost/myentity and to localhost/myentity/count, to be completed before refreshing all the views...
any idea how can I achieve it???
If your $ of choice is jQuery>1.5, you could take advantage of the deferred object to manually trigger a reset event when both calls have completed. Similar to your answer, but a bit more readable and without chaining the calls:
fetch: function() {
options = {silent: true, data: this.getParams()};
var _this = this;
var dfd_total = this.fetch_total();
var dfd_fetch = Backbone.Collection.prototype.fetch.call(this, options);
return $.when(dfd_total, dfd_fetch).then(function() {
_this.trigger('reset', _this);
})
},
fetch_total: function() {
// what you have in your question
}
And a Fiddle simulating these calls http://jsfiddle.net/rkzLn/
Of course, returning the results and the total in one fetch may be more efficient, but I guess that's not an option.
I think #nikoshr's answer is a good one so that you don't have to modify your API. If you think that you want to lessen your calls to the server, then consider returning an object from that endpoint that has paging information.
{
count: 1243,
page: 3,
per_page: 10,
results: [
...
]
}
and then overriding the collection's parse functionality
parse: function(res) {
this.count = res.count;
this.page = res.page;
this.per_page = res.per_page;
// return the collection
return res.results;
}
RESOURCES
http://backbonejs.org/#Collection-parse
I think I found a way to do it. What I did was to silently fire the fetch call, without triggering the 'reset' event
There, from the callback, I issue the fetch of the total (GET to localhost/myentity/count)
and from the total callback, I finally trigge the reset event
in code is something like this:
fetch: function() {
var that = this;
options = {
// will manually trigger reset event after fetching the total
silent: true,
data: this.getParams(),
success: function(collection, resp) {
that.fetch_total();
}
};
return Backbone.Collection.prototype.fetch.call(this, options);
},
fetch_total: function() {
var that = this;
var options = {
url: this.url + '/count',
data: this.getParams(),
contentType: 'application/json',
success: function(resp, status, xhr) {
that.total = parseInt(resp);
// manually trigger reset after fetching total
that.trigger('reset', that);
return true;
}
};
return $.ajax(options);
}
This is my first attempt, I wonder if there's an easier way
I am working on my first implementation of a jqGrid. I am using the standard add/edit buttons that appear in the navGrid but am having problems identifying how process the server response when I click Submit in the edit/add forms.
.navGrid("#product-codes-footer",{edit:true,add:true,del:false},
{afterShowForm:afterShowEdit}, {afterShowForm:afterShowAdd} );
Is there a standard callback or event parameter I am missing somewhere regarding this? Is there a way to define how saveRow is called or is there a default success/error callback method I can implement?
Any direction would be much appreciated!!!
There appears to be a couple event parameters that I failed to completely read and comprehend...
API --> http://www.trirand.com/jqgridwiki/doku.php?id=wiki:form_editing#editgridrow
using the event parameters for afterSubmit and afterComplete allow me to process the server response and update the form.
--Dan
EDIT
Here is an example of the code used...
.navGrid(
"#product-codes-footer",
{edit:true,add:true,del:false},
{
afterShowForm:afterShowEdit,
afterSubmit:processAddEdit,
beforeSubmit:validateData,
closeAfterAdd: true,
closeAfterEdit: true
},
{
afterShowForm:afterShowAdd,
afterSubmit:processAddEdit,
beforeSubmit:validateData,
closeAfterAdd: true,
closeAfterEdit: true
}
);
function afterShowEdit(formId) {
//do stuff after the form is rendered
}
function afterShowAdd(formId) {
//do stuff after the form is rendered
}
function processAddEdit(response, postdata) {
var success = true;
var message = ""
var json = eval('(' + response.responseText + ')');
if(json.errors) {
success = false;
for(i=0; i < json.errors.length; i++) {
message += json.errors[i] + '<br/>';
}
}
var new_id = "1";
return [success,message,new_id];
}
There are a few ways I have seen to do this:
jQuery("#search_results").jqGrid({
url: host,
datatype: "xml",
mtype: "GET", // Handy to see the params passed.
height: 200,
width: 500,
...
...
etc
gridComplete: function() {
var ids = jQuery("#search_results").getDataIDs();
if (ids.length Empty Result');
}
else {
$('#jqgrid_error').hide();
}
},
loadError: function(xhr,st,err) {
jQuery("#jqgrid_error").html("Type: "+
st +"; Response: "+ xhr.status + " "+xhr.statusText+'');
}
}).navGrid('#search_results_pager',
{edit:true,add:false,del:false,search:true},
{
afterComplete:processed, // processed is a function you define
closeAfterEdit: true,
reloadAfterSubmit:true
}
);
From the documentation:
afterComplete
This event fires immediately after all actions and events are completed
and the row is inserted or updated in the grid.
afterComplete(serverResponse, postdata, formid) where
response is the data returned from the server (if any)
postdata an array, is the data sent to the server
formid is the id of the form
gridComplete
This fires after all the data is loaded into the grid and all other
processes are complete.
loadError xhr,st,err
A function to be called if the request fails. The function gets passed
three arguments: The XMLHttpRequest object (XHR), a string
describing the type of error (st) that occurred and an optional
exception object (err), if one occurred.
There is a handy/helpful PDF documents (a little dated):
http://www.scribd.com/doc/17094846/jqGrid.
You could try this:
navGrid('#gridpager',{view:true},{},{closeOnEscape:true},{afterSubmit:processAddEdit});
$.jgrid.search={
odata : ['equal', 'not equal', 'less', 'less or equal','greater','greater or equal', 'begins with','does not begin with','is in','is not in','ends with','does not end with','like','does not contain'],
sopt:['eq','ne','cn','bw','ew']
}