In my ASP.NET Core web application, one of my pages has a sequence of steps that it performs to call stored procedures. Depending on whether or not stored procedures return rows, I route to one of two controller actions (either rendering a partial including an additional input, or overriding what that input would do and just coming back to the page on the next step).
Right now I've got code that is nearly there. My controller actions navigate and process correctly and my Ajax works... Sort of.
Button in the Razor view that calls the Ajax function
<input type="button" value="Run Check" onclick="runCheck('#actionItem.StepID', '#Model.Client.DatabaseConnectionString', '#Model.Client.ClientID')" />
Ajax
<script type="text/javascript">
function runCheck(x, y, z) {
$.ajax({
url: '#Url.Action("ProcessFeedbackHasRows", "Client")',
type: 'POST',
data: { stepId: x, databaseConnectionString: y, clientId: z },
success: function (result) {
if (result) {
alert('true');
var stepId = x;
var databaseConnectionString = y;
var clientId = z;
var url = '#Url.Action("ViewProcessingFeedBackPartial", "Client")';
$("#processingFeedbackPartialDiv").load(url, { stepId, databaseConnectionString, clientId },
function () {
$("#confirmButton").removeAttr("style");
});
} else {
alert('false');
var newUrl = '#Url.Action("Processing", "Client")';
window.location = newUrl;
}
}
});
};
</script>
Controller Action
public JsonResult ProcessFeedbackHasRows(int StepId, string DatabaseConnectionString, int ClientID)
{
bool hasRows = true;
FeedbackDetails feedbackDetails = new FeedbackDetails();
feedbackDetails.Data = _clientProcessingService.GetProcessingFeedbackDetails(StepId, DatabaseConnectionString);
if (feedbackDetails.Data.Rows.Count == 0)
{
_clientProcessingService.RunProcessStepConfirmation(DatabaseConnectionString, StepId, ClientID, "No information returned, automatically proceeding to next step.");
hasRows = false;
}
return new JsonResult (new { HasRows = hasRows });
}
The alerts are there to just prove that the right condition was in fact met and that the right things are happening. And this is where my problems lie. When I had the Network traffic tab of the F12 tools open, I noticed that whatever json object is created first determines all future runs of the code.
For example: let's say I forced the first item to come through with at least 1 row returned, I'd see the alert true, see the JSON object in the Network tab contain true and see my partial view, as expected.
The next several steps would produce a a false result because no rows were returned from the SP in the controller. The bool in the controller would be set to false, the JSON object in the Network tab would say HasRows = false, but my alert would show true and the partial still renders asking me for confirmation. So despite not returning any rows and producing a false result, I see the alert true and the partial is rendered even though in my Network tab I see
The opposite is true as well. If I had the first item through create an object where HasRows = false, and the next several would have been true, subsequent steps return true in the Network tab, but alert false and go through the false logic in the Ajax.
What is the best way to handle this? Is there a way to clear the JSON or something? I figured by creating a new JsonResult at the end of every method call, it would produce a new result to inspect, but it seems to continue using the first one sent in despite being able to see the others in the Network tab.
What I've tried
Disabling cache in Ajax by adding cache: false, right above the URL in the $.ajax setup.
Resetting the json object within the function after my else braces
result = []; and delete result.hasRows;
Despite these attempts, the ajax will always alert and go through whatever logic flow was sent first while the actual object contains the correct variable.
I actually solved this because in my inexperience with javascript and jQuery, I didn't understand exactly how the logic was being handled since I can't very easily debug the javascript the way I can the C#.
Through trial and error and alerts, I found that essentially, the in my Ajax I had to change the if condition to inspect result.hasRows rather than just the result.
<script type="text/javascript">
function runCheck(x, y, z) {
$.ajax({
cache: false,
url: '#Url.Action("ProcessFeedbackHasRows", "Client")',
type: 'POST',
data: { stepId: x, databaseConnectionString: y, clientId: z },
success: function (result) {
if (result.hasRows) {
alert('result = true');
alert('result.hasRows = ' + result.hasRows);
var stepId = x;
var databaseConnectionString = y;
var clientId = z;
var url = '#Url.Action("ViewProcessingFeedBackPartial", "Client")';
$("#processingFeedbackPartialDiv").load(url, { stepId, databaseConnectionString, clientId },
function () {
$("#confirmButton").removeAttr("style");
});
} else {
alert('result = false');
alert('result.hasRows = ' + result.hasRows);
var newUrl = '#Url.Action("Processing", "Client")';
window.location = newUrl;
}
}
});
};
</script>
The initial question still stands though. Assuming I wanted to delete the entire JSON object and use my initial logic present in the question, how can I clear or delete the entire object so that every time I hit that Ajax call, a new object would be inspected? Or is that now how this works and I solved the problem correctly?
Related
Here i am trying to open the file in new tab by calling ViewFile action of Doctor controller using Ajax Success which is in functionabc(this) on click of anchor tag.
Now the problem is that everything is as required but the url doesnot open in new tab.
Below is my Ajax
<script>
function abc(thisEvent) {
debugger;
var getDoCredId = $(thisEvent).attr('docCredId');
var parameter = { id: getDoCredId };
$.ajax({
url: "/Doctor/ViewFile1",
type: "get",
dataType: "html",
data: parameter,
success: function (data) {
debugger;
if (data = true) {
debugger;
var getdoctorId = $(thisEvent).attr('docCredId');
var url = "/Doctor/ViewFile/" + getdoctorId;
window.open(url, "_blank");
}
else {
debugger;
showNotification("Error", "warning");
}
}
});
}
Below is my anchor tag HTML
<a title="View Attachment" docCredId = "' + getDocCredId + '" onclick="abc(this)"><i class="btn btn-web-tbl btn-warning fa fa-eye "></i></a>
Below is code behind
public bool ViewFile1(int id)
{
var document = _doctorService.GetDoctorCredentialDetails(id);
string AttachPath = ConfigPath.DoctorCredentialsAttachmentPath;
string strFileFullPath = Path.Combine(AttachPath, document.AttachedFile);
string contentType = MimeTypes.GetMimeType(strFileFullPath);
bool checkFileInFolder = System.IO.File.Exists(strFileFullPath);
if (checkFileInFolder == true)
{
return true;
}
else
{
return false;
}
}
public ActionResult ViewFile(int id)
{
var document = _doctorService.GetDoctorCredentialDetails(id);
string AttachPath = ConfigPath.DoctorCredentialsAttachmentPath;
string strFileFullPath = Path.Combine(AttachPath, document.AttachedFile);
string contentType = MimeTypes.GetMimeType(strFileFullPath);
bool checkFileInFolder = System.IO.File.Exists(strFileFullPath);
bool filedata = System.IO.File.ReadAllBytes(strFileFullPath).Any();
byte[] filedata1 = System.IO.File.ReadAllBytes(strFileFullPath);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = document.FileName,
Inline = true
};
Request.HttpContext.Response.Headers.Add("Content-Disposition", cd.ToString());
return File(filedata1, contentType);
}
Since this is too long for a regular comment, I am posting this as an answer, although it isn't directly going solve the problem because I am not able to reproduce it, but might give some insights and let you check the differences with what happens in your code as compared with this simplified example.
Calling window.open() from jQuery ajax success callback works just fine: https://codepen.io/nomaed/pen/dgezRa
I used the same pattern as you did, without your server code but using jsonplaceholder.typicode.com sample API instead.
There are some issues with the code sample that you might want to consider, even though you didn't ask for comments about it and it's not directly related to your issue (probably):
if (data = true) means data will always be true. You probably mean to do a if (data === true) if you know it's a boolean value, or if (data) if you want to accept any truthy value (true, {}, "something", 42, etc). Judging by the Java code and how you define the response format in the jQuery ajax call, it looks like you're expecting the "data" variable result be an HTML and not a boolean. You might want to try and remove the dataType: "html" row and let jQuery set the data format according to what is coming back from the server, and/or send a JSON formatted response, as in a POJO of { result: true } for a successful response. Then make sure that data.result === true to be sure that you got what you expect.
You should probably add arbitrary data to tags DOM elements the data-* attributes and if you're using jQuery, access them using the .data() selector. White adding just random attributs with string values may work, it's considered an abuse of the HTML and DOM, and the data-* attributes are there specifically for adding any data.
In the abc() function you grab the value of the attribute in the beginning (var getDoCredId = $(thisEvent).attr('docCredId');) but in the callback you're trying to get the value once more. You really don't need it since the success() callback is a closure in the scope of the abc() function and it has access to the value already, so doing var getdoctorId = $(thisEvent).attr('docCredId'); in the callback is really not needed.
I'd also suggest naming getDoCredId variable just as docCredId. Having a "get" prefix usually means that it's a getter function or a reference to some getter. Likewise, the "thisEvent" argument of the main function should probably be called "callerElement" or something like that since it's not an event, it's an actual element that you're passing directly from the DOM when calling abc(this) in the onClick event handler of the <a> anchor. This is just to make the code clearer to understand for anyone who's reading it, and for yourself when you're coming back to it several months in the future and trying to figure out what's going on :)
Try adding async: false to your Ajax request
function abc(thisEvent) {
debugger;
var getDoCredId = $(thisEvent).attr('docCredId');
var parameter = { id: getDoCredId };
$.ajax({
async: false, // <<<----------- add this
url: "/Doctor/ViewFile1",
type: "get",
dataType: "html",
data: parameter,
success: function (data) {
debugger;
if (data = true) {
debugger;
var getdoctorId = $(thisEvent).attr('docCredId');
var url = "/Doctor/ViewFile/" + getdoctorId;
window.open(url, "_blank");
}
else {
debugger;
showNotification("Error", "warning");
}
}
});
}
So I have this Javascript code:
function AddToStuff(somethingID) {
var stuffID = document.getElementById('StuffID').innerText;
alert("First Alert: " + stuffID);
if (stuffID == -1) {
document.getElementById('StuffID').innerText = 0;
stuffID = 0;
}
alert("Second Alert: " + stuffID)
StuffCallback.fire(somethingID, stuffID);
}
$(document).ready(function () {
initializeStuff();
});
var StuffCallback = $.Callbacks();
function initializeStuff() {
StuffCallback.add(function (somethingID, stuffID) {
$.ajax({
url: "pos/Stuff",
type: "GET",
traditional: true,
data: {
somethingID: somethingID,
stuffID: stuffID
},
success: function (result, textStatus, xhr) {
alert("Third Alert: " + stuffID);
var contentArea = $("#Stuff");
contentArea.html(result);
$("#Stuff").hide();
$("#Stuff").show({ duration: 250 });
}
});
});
}
And this C# code:
public ActionResult Stuff(int somethingID = 0, int stuffID = -1)
{
if (stuffID == -1)
{
//do something
}
else if (stuffID == 0)
{
//do something else
}
else
{
//do something else
}
}
return View();
}
}
The problem is that the C# method always has stuffID at -1 and so performs the first "if" case.
When the page loads and $(document).ready calls initializeStuff, the stuffID is -1, so it performs the first "if" case, as desired. Later, when AddToStuff() is called, because the user clicked on something, it reads in my element StuffID, which I have initialized to -1. The First Alert does display the -1. Then StuffID gets changed to 0 in the "if" inside AddToStuff() and the Second Alert does display the 0.
But then I have the Javascript function AddToStuff call the C# function again, but the goal is to have the second parameter (stuffID) be the 0 instead of the -1. And even the Third Alert will display a 0. But in the C# function, the stuffID is still -1.
I can't pass in the 0. Any help would be appreciated.
Note: I generalized my code into "something" and "stuff", but that part should be working fine - the functions call when I expect them to.
Suggestions:
Use fiddler or developer tools in your browser to see what is being passed to the server (0, or -1)?
The default value to Stuff(int somethingID = 0, int stuffID = -1) can also be causing the problem if the data received by the server is not correct.
Set the data type in your Ajax call dataType: 'json'
If you are using a GET request, make sure that you have set the data object correctly
If I comment all your JS code and just write
StuffCallback.fire(0, 5);
it works nicely, as it accepts 5. That means that your stuffID is not being recognized as int. What kind of a HTML element is it? I see you are using .innerText. As you are using jQuery already, why not use
var stuffID = $('#StuffID').val();
I have noticed your problem though. You have a typo.
getElementById('StuffID') and your p is called "stuffID". JS IS case sensitive, so it will be enough to fix that, but please use
<input type="hidden" ID="StuffID" value="#stuffID">
as that's proper HTML markup
I need a cross domain web api method to return valid jsonp to some javascript from C#. I can't seem to make this magic happen. I've looked around the web and can't find a start to end example that fits my needs and works... Fiddler shows that I'm returning valid json data but when I hit a breakpoint in F12 dev tools or firebug the result is a failure message.
Here is what I've currently got:
C#
/// <summary>
/// POST: /Instance/RefreshItem
/// </summary>
/// <param name="instanceId"></param>
/// <returns>Json</returns>
[HttpPost]
public System.Web.Mvc.JsonResult RefreshItem(int instanceId, Guid customerId)
{
try
{
var clientConnection = Manager.ValidateInstance(customerId, instanceId);
clientConnection.RefreshItem();
var result = new MethodResult()
{
Success = true,
Value = instanceId,
Message = "Item successfully refreshed."
};
return new System.Web.Mvc.JsonResult() { Data = result };
}
catch (Exception ex)
{
Manager.LogException(_logger, ex, customerId, instanceId);
var result = new MethodResult()
{
Success = false,
Value = instanceId,
Message = ex.GetBaseException().Message
};
return new System.Web.Mvc.JsonResult() { Data = result };
}
}
JS
Example.RefreshItem = function ()
{
Example.SDK.JQuery.getSettings(
function (settings, userId, userLocaleId)
{
alert("Attempting to refresh item for instance " + settings.ConnectionId + "\r\nThis may take awhile.");
var url = settings.SystemUrl + "/Api/WebApiServices/ExampleAdmin/RefreshItem?customerId=" + settings.CustomerId + "&instanceId=" + settings.ConnectionId;
$.ajax({
url: url,
dataType: "jsonp",
jsonpCallback: 'RefreshItemCallback',
success: RefreshItemCallback
})
},
Example.SDK.JQuery.defaultErrorCallback
);
}
function RefreshItemCallback(data)
{
alert(data.d.Message);
}
I've also tried $.Post().Always() with the same results.
What am I doing wrong???
I think your problem is that you're instantiating a JsonResult instead of using the Json method.
Presumably the C# method you have is in a controller, so instead of
return new System.Web.Mvc.JsonResult() { Data = result };
do:
return Json(result);
This method probably sets some of the other properties of the JsonResult that, when not set, will not be properly received by the client.
See how Microsoft only shows you how to create a JsonResult via the Json method on MSDN
Note that the same is probably true with methods like View, Content, and File.
Fight all week unable to find an answer until you ask the question somewhere... Within 30 minutes of asking I found this: http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/ which was exactly what I needed.
Thanks to all who posted.
I'm working on creating a Users collection with the ability to then grab single users inside. This will be used to match from another system, so my desire is to load the users once, and then be able to fine/match later. However, I'm having a problem accessing the outer users collection from an inner method.
function Users(){
var allUsers;
this.getUsers = function () {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
allUsers = data;
}
});
return allUsers;
};
this.SingleUser = function (name) {
var rate = 0.0;
var position;
this.getRate = function () {
if(position === undefined){
console.log('>>info: getting user position to then find rate');
this.getPosition();
}
$.ajax({
url: '../app/data/rates.json',
async: false,
dataType: 'json',
success: function(data) {
rate = data[position];
}
});
return rate;
};
this.getPosition = function () {
console.log(allUsers);
//position = allUsers[name];
return position;
};
//set name prop for use later I guess.
this.name = name;
};
}
and the test that's starting all of this:
it("get single user's position", function(){
var users = new Users();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
The getPosition method is the issue (which might be obvious) as allUsers is always undefined. What I have here is yet another attempt, I've tried a few ways. I think the problem is how the Users.getUsers is being called to start with, but I'm also unsure if I'm using the outer and inner vars is correct.
Though the others are correct in that this won't work as you have it typed out, I see the use case is a jasmine test case. So, there is a way to make your test succeed. And by doing something like the following you remove the need to actually be running any kind of server to do your test.
var dataThatYouWouldExpectFromServer = {
bgrimes: {
username: 'bgrimes',
show: 'chuck',
position: 'mgr'
}
};
it("get single user's position", function(){
var users = new Users();
spyOn($, 'ajax').andCallFake(function (ajaxOptions) {
ajaxOptions.success(dataThatYouWouldExpectFromServer);
});
users.getUsers();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
This will make the ajax call return whatever it is that you want it to return, which also allows you to mock out tests for failures, unexpected data, etc. You can set 'dataThatYouWouldExpectFromServer' to anything you want at any time.. which can help with cases where you want to test out a few different results but don't want a JSON file for each result.
Sorta-edit - this would fix the test case, but probably not the code. My recommendation is that any time you rely on an ajax call return, make sure the method you are calling has a 'callback' argument. For example:
var users = new Users();
users.getUsers(function () {
//continue doing stuff
});
You can nest them, or you can (preferably) create the callbacks and then use them as arguments for eachother.
var users = new Users(), currentUser;
var showUserRate = function () {
//show his rate
//this won't require a callback because we know it's loaded.
var rate = currentUser.getRate();
}
var usersLoaded = function () {
//going to load up the user 'bgrimes'
currentUser = new users.SingleUser('bgrimes');
currentUser.getRate(showUserRate);
}
users.getUsers(usersLoaded);
your approach to fill the data in allUsers is flawed
the ajax call in jquery is async so every call to users.getAllUsers would be returned with nothing and when later the success function of the jquery ajax is called then allUsers would get filled
this.getUsers() won't work. Its returning of allUsers is independent from the ajax request that fetches the data, because, well, the ajax is asynchronous. Same with getRate().
You'll have to use a callback approach, where you call getUsers() with a callback reference, and when the ajax request completes, it passes the data to the callback function.
Something like:
this.getUsers = function (callback) {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
callback(data);
}
});
};
And the call would be along the lines of:
var user_data = null;
Users.getUsers(function(data) {
user_data = data;
});
I am still trying to figure all this out and I am coming across a really weird error.
I was using getJSON but after searching for solutions to this problem, I found that it was better to try to use the AJAX function (for error capturing -> which isnt firing).
Using breakpoints in firebug, if I go slowly through the running code, it works (mostly) fine (just need to change some coordinates for better drawing). But if I let it run at normal speed, it attempts to do the callback before the json object is returned. The firebug console says everything works ok (code 200), but when examining the jobj inside ship object/function it appears to be "undefined or null"
Following the breakpoints, the ajax calls seem to be going to "error" and not "success". But it isn't firing the alert...
Also, the response takes like 300-500ms.... is that normal? or do I need to find a better server?
Edited Code:
var init = (function(){
thisplayer = new player();
jQuery.ajax({type: "GET", url: "shipdata.php", processData: true, data: {shipid:1}, dataType: "json",
success: function(json) {
var pship = new ship(json);
player_ship = $.extend(thisplayer, pship);
starfield = new starfield();
for(var i = 0; i < player_ship.enemytotal; i++) {
$.ajax({
type: "GET",
url: "shipdata.php",
processData: true,
data: {shipid:Math.round((Math.random()*2+2))},
dataType: "json",
success: function(json) {
var enemy = new ship(json);
game.enemies.push(enemy);
},
error: function(x,y,z) {
// x.responseText should have what's wrong
alert(x.responseTest);
}
});
}
game.initialized = true;
},
error: function(x,y,z) {
// x.responseText should have what's wrong
alert(x.responseTest);
}
});
})
..............................
var ship = (function(json){
var self = this;
jobj = jQuery.parseJSON(json.responseText);
self.height = jobj.height;
self.width = jobj.width;
self.xinit = jobj.xinit;
self.yinit = jobj.yinit;
self.speed = jobj.speed;
self.weapons = jobj.weapons;
self.maxlasers = jobj.maxlasers;
self.imagesrc = jobj.imgurl;
self.lasers = [];
self.x = self.xinit;
self.y = self.yinit;
JSON being sent in:
{"height":75,"width":50,"xinit":275,"yinit":525,"speed":3,"weapons":[1,2],"maxlasers":2,"imgurl":"images\/ship.png"}
Live Demo:
http://www.schennshome.net/medicalmmj/practice/index.html (The code is far from being perfect, Im running through it to try and catch the various errors before animating, but cant get past this.)
I've dug through your source code, and the problem is not actually shown in your question. The problem is with this line, which follows your Ajax call:
player_ship = $.extend(thisplayer, game.pship);
game.pship refers to the data returned by the ajax call, but since this is asynchronous, the above line will be evaluated first, which means game.pship will not be defined.
To fix this, you need to include all of the code in your init function that is currently below the ajax call directly in the success callback. This will prevent the code from being evaluated until the ajax call has returned.
The reason that it works with breakpoints is that this interrupts evaluation, which allows the ajax call to complete before game.pship is referenced.
Edit
I'm now getting an error on line 489, stating that player_ship is undefined. This is again because of the evaluation order of async code. The problem is that player_ship is being defined inside the ajax function in init now, but is being referenced in gameLoop, outside of this callback.
This is how I would rewrite gameLoop:
var callback = function() {
game.canvas.clearCanvas();
drawStarfield();
if(player_ship.alive && game.initialized && !(game.loading)) {
drawPlayer();
drawLaser();
drawEnemies();
}
};
if(game.initialized==false) {
init(callback);
} else {
callback();
}
And then amend init to accept a callback method which is called at the bottom of your success callback. This way, if the game has not been initialized (and player_ship is not yet defined), it will be executed after the ajax call.