C# MVC Razor 5: location.href in an ajax call - javascript

I have a solution in my MVC project which allows me to show and/or hide a loading screen whenever I want to:
$("#divLoading").show();
$("#divLoading").hide();
This "divLoading" essentially contains the loading screen. Now this solution works fine most of the time; even if I want to use Ajax in order to show the loading screen when the script begins execution and hide it when the function is done. Something like:
$("#btnTreatData").click(function () {
$("#divLoading").show();
// Some stuff
$.ajax({
contentType: 'application/json; charset=utf-8',
url: '#Url.Action("TreatValidationData", "Article")',
type: "POST",
data: JSON.stringify({ listOfCheckedData: listOfCheckedData }),
success: function (result) {
$("#tableControl").html(result);
$("#divLoading").hide();
}
});
}
This works fine. However, there is one specific edge-case where I can't get this to work properly.
Essentially, this project uses a plugin named EEPlus (http://epplus.codeplex.com/) in order to export data into an XLS file. It's very practical for the user because they simply click on the button, and there's no redirection involved; when the file is done, the user is prompted to download it by the browser itself.
Essentially, my export method does something like this:
public void ExportListUsingEPPlus(string filter, string type)
{
// A boat load of data processing and formatting that isn't relevant here
// Once the work is done:
// Write in an Excel file
using (var memoryStream = new MemoryStream())
{
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", $"attachment; filename={filename}.xlsx");
excel.SaveAs(memoryStream);
memoryStream.WriteTo(Response.OutputStream);
Response.Flush();
Response.End();
}
}
You will notice this method doesn't return anything. It doesn't need to; in the View itself, it is called like this:
<li class="separator"><input value="Export xls" class="add" name="btn_submit" id="btn_excel" type="button" onClick="location.href='#Url.Action("ExportListUsingEPPlus", "Article", new { filter = #ViewBag.CurrentFilter, type="TEST"})'"></li>
Which is okay... except that if I try to use a loading screen for this, because exporting data can take a long while:
function exportWithLoadingScreen()
{
$("#divLoading").show();
$.ajax({
contentType: 'application/json; charset=utf-8',
url: '#Url.Action("ExportListUsingEPPlus", "Article", new { filter = #ViewBag.CurrentFilter, type = "TEST" })',
type: "POST"
}).complete(function (result) { $("#divLoading").hide();});
}
Ajax :
Doesn't proc a "success" event.
Doesn't proc a "failure" event.
Procs a "complete" event... but, of course, without location.href = '...', it doesn't actually do anything (the file is created in memory but the user is never prompted to download it).
If I try to use the same thing but caring about location.href, it procs the "complete" event far too early, possibly because it only cares when the redirection is complete, NOT when the method is done!
I really don't know what else I could try at this point, but it's so important to show a loading screen for this, because it really can take a long while.
Any ideas as to what else I could do? I'd appreciate it a lot!
EDIT: I'll be more precise and concise. This:
function exportWithLoadingScreen() {
$("#divLoading").show();
location.href = '#Url.Action("ExportListUsingEPPlus", "Article", new { filter = #ViewBag.CurrentFilter, type = "TEST" })';
$("#divLoading").hide();
}
Doesn't work, because all actions are simultaneous, so I have to use ajax. But this:
function exportWithLoadingScreen() {
$("#divLoading").show();
$.ajax({
contentType: 'application/json; charset=utf-8',
url: '#Url.Action("ExportListUsingEPPlus", "Article", new { filter = #ViewBag.CurrentFilter, type = "TEST" })',
type: "POST"
}).complete(function (result) { $("#divLoading").hide(); })
};
Doesn't work either, because location.href is NOT executed (=> user is not prompted for download). But THIS:
function exportWithLoadingScreen() {
$("#divLoading").show();
$.ajax({
contentType: 'application/json; charset=utf-8',
url: '#Url.Action("ExportListUsingEPPlus", "Article", new { filter = #ViewBag.CurrentFilter, type = "TEST" })',
type: "POST"
}).complete(function (result) { window.location.assign(result);})
};
Doesn't work, because the returned data is completely wrong (server error), because the ExportListUsingEPPlus method doesn't return anything.
EDIT EDIT: Edited question's title to refocus the question.

I think the confusion is made by the fact that you want to use an Ajax call, but also need to redirect the page.
Ajax is used to request data from the server back to the current page so that you can update the current page with the new data.
A redirect abandons the current page and replaces it with a new one entirely.
Here is another answer that explains it, with something that can simulate an Ajax request - but it still isn't an Ajax request if it is redirecting...
https://stackoverflow.com/a/9970672/4619012

You can get help from calling ajax events. Find it here https://api.jquery.com/Ajax_Events/

Related

Ajax request not passing parameter to ASP.NET MVC controller

Tried looking in stackoverflow because this looked so trivial. Found many similar questions and read through them. Found no solution using these examples. Here is my code, can anyone help?
function testAjax() {
return $.ajax({
type: "GET",
url: '#Url.Action("Nodes","Competence", new { userId = Sven });',
contentType: "application/json;charset=utf-8",
dataType: "json"
});
}
var promise = testAjax();
promise.success(function (data) {
var dataConverted = JSON.stringify(data);
$('#tree').treeview({ data: dataConverted, multiSelect: true });
});
ASP.NET MVC method
public JsonResult Nodes(string userId)
{
var temp = userId;
var list = new List<Node>();
list.Add(new Node("Test1"));
list.Add(new Node("Test2"));
list.Add(new Node("Test3"));
return Json(list, JsonRequestBehavior.AllowGet);
}
EDIT:
Just before I was about to turn crazy on Halloween night, i figured out to try in a new session. Turns out it was just a caching problem..Thanks for the help everyone
Since your server may not expecting a request with JSON content, try removing the contentType parameter on your ajax call. Its default value is "application/x-www-form-urlencoded; charset=UTF-8" and is fine for most cases.
It's type should be "POST"
return $.ajax({
type: "POST",
url: '#Url.Action("Nodes","Competence")',
data: { userId: "Test" },
contentType: "application/json;charset=utf-8",
dataType: "json"
});
As it's a GET verb, it'll be easiest to pass this in as a querystring value. This also conforms better with a RESTful design.
For example replace #Url.Action("Nodes","Competence")
with
#Url.Action("Nodes","Competence", new { userId = id });
Then you can delete the data property. This will append ?userId=valueOfId into your url and then it should be mapped correctly to your action with the userId correctly populated.
Update
As #freedomn-m stated:
This will generate the url when the view is built server-side. If the
parameters never change, then fine - but it's relatively unlikely that
the parameters won't change, in which case you should add the url
parameters at runtime if you want them on querystring.
This is completely accurate. Without knowing your exact implementation I can only make assumptions. But technically you could wrap your ajax call in a function and then you could either pass in the userId and generate the url within that function or pass in the url, performing the url generation outside of the function.
This would mean that you only need one function that performs the ajax request and you can have another function that gets the userId (and possibly generates the url) and then passes that into the ajax function. How you store the userId is entirely up to you, but one thing I would suggest is investigating data attributes which is a fairly well defined way for storing data on html elements.

Passing a string array to mvc controllers using ajax

I need to pass list of strings from multiple select to the controller. Though the requirement looked very simple to me, I was breaking my head for the past hour in this. I have did a fair research on this, but unable to succeed.
Below is my Javascript code. Please ignore the comments. I was successfully able to fetch the list of items in the multiple select. While i do the ajax call, I get the error "Object reference not set an instance of an object.
function submitForm() {
var selected = $('#selectedTasks option').map(function(){
return this.value
}).get()
var postData = { selectedTasks : selected } //corrected as suggested
//selectedTasks = JSON.stringify({ 'selectedTasks': selected });
alert(postData);
$.ajax({
type: "POST",
//contentType: 'application/json; charset=utf-8',
url: '#Url.Action("AssignTasks", "MonthEndApp")',
dataType: 'json',
data: postData,
traditional: true,
success: function (data) {
alert("Success");
},
error: function (xhr) {
alert(xhr.responseText);
}
});
}
MonthEndAppController.cs
[HttpPost]
public void AssignTasks(List<String> selectedTasks)
{
//do something
}
Can someone guide me where exactly I have gone wrong? Can someone suggest me what is wrong?
EDIT : As suggested by Mr. Rory I have made the java script changes. Now the Java script part works absolutely fine. But the Controller is not getting called when the ajax request is made. Can someone help me out if something wrong in the call made to controller ?
Have you tried with string[] instead of List<String> ?
The parameter your AssignTasks action is expecting is called selectedTasks, not values:
var postData = { selectedTasks: selected };
Also note that when debugging anything in JS you should always use console.log() over alert(), as the latter coerces all types to a string, which means you're not seeing a true representation of the actual value.

MVC Jquery/Controller Post Redirect

I'm struggling to achieve the following, I have a page where a user Logs a Call, the user needs to input various fields and selects from several dropdowns, I then need to post that data (either via JQuery or the controller) to another page, where the user can view the entered data and decide to commit it or not.
I've been going back and fourth for ages now, trying to figure out how to post data from my cshtml to my controller and then redirect to another page with that data persisting.
I've tried to redirect via JQuery and/or the controller and just can't seem to get one or the other working.
Code extracts below:
cshtml:
$.ajax({
url: dir + '/Submit/',
async: true,
type: 'POST',
data: JSON.stringify(callData),
contentType: 'application/json; charset=utf-8',
complete: function () { },
success: function (data) {
}
})
Controller:
[HttpPost]
public ActionResult Submit(SupportCallModel callData)
{
SupportCallModel newData = new SupportCallModel();
newData.SupportCallID = 1;
newData.CallTypeID = callData.CallTypeID;
newData.TroubleShooting = callData.TroubleShooting;
newData.EmailRequest = callData.EmailRequest;
newData.MailDate = callData.MailDate;
newData.FSEOnSite = callData.FSEOnSite;
newData.FSEEmployeeID = callData.FSEEmployeeID;
newData.CallCategory = callData.CallCategory;
newData.CallType = callData.CallType;
newData.CallItem = callData.CallItem;
newData.Summary = callData.Summary;
newData.Description = callData.Description;
newData.ExternalReference = callData.ExternalReference;
newData.CallStatusID = callData.CallStatusID;
newData.CallPriorityID = callData.CallPriorityID;
newData.CallCoEmployeeID = callData.CallCoEmployeeID;
return RedirectToAction("Verify", newData);
}
public ActionResult Verify(SupportCallModel postData)
{
return View(postData);
}
Using ajax is pointless since ajax calls stay on the same page (return RedirectToAction("Verify", newData); is ignored). You can just do a normal submit. Assuming your rendering all the required inputs for SupportCallModel in view, then it will post back. I would recommend you include
[HttpPost]
public ActionResult Submit(SupportCallModel callData)
{
if (!ModelState.IsValid)
{
return View(callData);
}
...
at the top of the method in case the model contains validation errors.
You then create a new instance of SupportCallModel based on the properties of callData which also seems pointless (why not just pass callData instead of newData?)
If SupportCallModel contains only properties which are Value types then you can use return RedirectToAction("Verify", newData); or return RedirectToAction("Verify", callData);. Internally a RouteValueDictionary is created based on the name and value of each property and postData will be correctly bound in the Verify() method. If however any of the properties are complex types or collections then binding will fail for those properties. In that case you need to persist the model so it can be retrieved in the Verify method. My recommendation would be to persist to the database (either a separate table or the existing table that includes a field indicating a pending status), but you could use Session or TempData (in conjuction with .Peek so its not lost if the user hits the refresh button).
I'm not sure exactly what the Verify GET method is rendering, but if it does not include controls for all properties then the Verify submit button will need to post back some ID value that allows you the retrieve the model again from the database or session and finally save it to the database.
It's not working because you're redirecting on the server side when you're making the call via AJAX.
Your redirects should be made on the client side since you're calling the ActionResult on a non-traditional sense (AJAX).
You can remove the return RedirectToAction("Verify", newData); from your action result since it will not do anything. You can probably just return something that specifies whether the call was valid or not.
For your data to persist on another page, you will have to save the data into a temp table in DB so you can show it when you do a redirect.
$.ajax({
url: dir + '/Submit/',
async: true,
type: 'POST',
data: JSON.stringify(callData),
contentType: 'application/json; charset=utf-8',
complete: function () {
},
success: function (data) {
if (data && data.isValid) {
// Grab the tempId that was saved temporarily for verification.
var tempId = data.tempId;
// Perform redirect
window.location = dir + '/Verify/' + tempId;
}
}
});
In your url post...
$.ajax({
url: '#Url.Action("Submit","{ControllerName}")',
async: true,
type: 'POST',
data: JSON.stringify(callData),
contentType: 'application/json; charset=utf-8',
complete: function () { },
success: function (data) {
window.location.href = '#Url.Action("Verify","{ControllerName}", Model);'
}
})
You could model this all without Ajax using standard form posts to MVC controllers reasonably simply.
Assuming your flow looks something like this:
Make the Submit POST return a view containing the data sent in the model. If that data can be verified make the view allow the data on that form to be posted to a Confirm controller action.
In this design the data is entirely transient, the data sent in the HTTP form payload on the initial post is then returned as a form within the returned HTML. This data is then in turn sent to the Verify action.
To do this in your case I think it might be as simple as calling the Submit with the Post verb as a none Ajax call and amending it so the return line looks like return View("Verify", newData);
You will also obviously need to do something else with Verify to do something in that action method.

Why is my jquery ajax not working on my page and page is also refreshing

I am new in the area of jQuery/Ajax and my little test function doesn't work. And my page is also refreshingcan any one help me
<script type="text/javascript" >
$(document).ready(function() {
$("#ser_itm").change(function() {
var id=$(this).val();
var dataString = 'id='+ id;
alert(dataString);
$.ajax({
type: "POST",
url: "bar_pull.php",
data: dataString,
cache: false,
success: function(html) {
$("#tbl").html(html);
}
});
});
});
Pass the function, not the result of the function call:
$('.linkDetails').on('click', getDetailsFromServer);
Apply the same to your AJAX success callback:
success: postToPage
Also, the getDetailsFromServer() function needs to be defined before you bind it to an event. Move the function declaration before your .on('click', ...) call.
So I'm going to try and explain these points more clearly:
You cannot access C:\Users\yah\Desktop\text.txt. This is a server side path, your javascript runs on the client side. So this needs to be a path you can browse to in your browser, something like /pathinURL/text.txt. How you do this is dependant on your hosting technology, etc.
Your call backs are also wrong,
$('.linkDetails').on('click', getDetailsFromServer());
&
success: postToPage()
these will execute the function when they are hit, (well it actually binds the function result) not when the event happens. To make these work you need to remove the braces:
$('.linkDetails').on('click', getDetailsFromServer);
&
success: postToPage
this then hooks up the actual functions as function pointers and thus the actual functions will be fired when you want them to be.
so your final code will look like:
$('.linkDetails').on('click', getDetailsFromServer);
function getDetailsFromServer() {
$.ajax({
type: 'GET',
url: '/someURL/text.txt',
success: postToPage
});
}
function postToPage(data) {
$('.textDetails').text(data);
console.log(data);
}
what Arun P Johny said is right! but your code has another probloem
$('.linkDetails').on('click', getDetailsFromServer);
try above
The same origin policy implemented by browsers prevents local file system urls... if the page and the files are in same folders it might work.
See SOP for file URI for FF

Get AJAX data from server before document ready (jQuery)

I want take some data from server and write it to global array in JavaScript. Then in document ready I want to use this array to create some new elements (options). I should have global array with this data, because after first load client can modify user interface using this data.
$(document).ready(function () {
UseAjaxQueryForFillGlobalArray();
MakingInterfaceUsingGlobalArray();
});
But I have strange behavior, when I debug page, I can see that method MakingInterfaceUsingGlobalArray working first, and just after I get data via AJAX with method UseAjaxQueryForFillGlobalArray and I don't have new interface(html options) with loaded data.
If I do like this:
UseAjaxQueryForFillGlobalArray();
$(document).ready(function () {
MakingInterfaceUsingGlobalArray();
});
Then in Firefox working fine, but in another web-browsers incorrect in first load (for example go to this page by link). But if I refreshing by F5, I have correct user interface which loaded via AJAX to global JS array.
How to fix it? Maybe I using totally incorrect way?
Added after comments:
This is my ajax function:
function UseAjaxQueryForFillGlobalArray(){
var curUserId = '<%= Master.CurrentUserDetails.Id %>';
var curLocale = '<%= Master.CurrentLocale %>';
$.ajax({
type: "POST",
url: "/segment.aspx/GetArrayForCF",
data: '{"userId":"' + curUserId + '","curLocale":"' + curLocale + '"}',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
//here is I doing parse my string from server and fill arrays.
}
});
}
I think that the problem is that you don't know exactly when the first function returns, since it'a asynchronous. So you should use the array in the callback only
function UseAjaxQueryForFillGlobalArray() {
// make the call
$.post(url, data, function() {
// let's be sure that the dom is ready
$(document).ready(function () {
// use the array
MakingInterfaceUsingGlobalArray();
}
}
}();// invoke the function
It's like reviving this post from the dead, but I had the same problem today, jQuery version greater than 1.6 has this ability:
https://api.jquery.com/jquery.holdready/
And I've used it like this:
$.holdReady(true);
var remoteJSONContent = null;
$.getJSON("http://www.example.com/remote.json", function(data) {
remoteJSONContent = data;
$.holdReady(false);
});
$(document).ready(function(){
console.log(remoteJSONContent);
});
Without using holdReady, I was getting null, after, I got the content.
For anyone still searching the answer for this.

Categories

Resources