Usually, in ASP.NET, MVC or otherwise, we make the client download a file by having the user click a link/button; I assume this link/button action goes to the correct controller then redirects back to the main page. However, how can I cause the download from some intermediate controller, during a series of redirects?
Basically, when the user clicks a button to create a PDF, the action goes to PdfController to create the PDF, then, since I'm assuming he/she wants to download the PDF anyway (if he/she doesn't, he/she can always click "No"), I want to have the browser download the PDF before the page gets rendered again. If I haven't lost you yet, how do I accomplish this?
Here's a sample of what I have so far. Button that starts the action:
<a class="btn btn-primary col-md-2 col-md-offset-1" href="#Url.Action("MakePdf", "Pdf")">Create PDF</a>
PdfController's MakePdf() method:
public ActionResult MakePdf()
{
string PdfUrl = Environment.GetEnvironmentVariable("HOMEDRIVE") + Environment.GetEnvironmentVariable("HOMEPATH") + "/Sites/Bems/PDF/UserPdfs/report" + Id + ".pdf";
// create the PDF at this PdfUrl
return RedirectToAction("ShowPdf", "Pdf", new { PdfUrl = PdfUrl });
}
PdfController's ShowPdf() method, redirected from the previous MakePdf() method:
public ActionResult ShowPdf(string PdfUrl)
{
if (System.IO.File.Exists(PdfUrl))
{
return File(PdfUrl, "application/pdf"); // Here is where I want to cause the download, but this isn't working
}
else
{
using (StreamWriter sw = System.IO.File.CreateText(PdfUrl))
{
sw.WriteLine("A PDF file should be here, but we could not find it.");
}
}
return RedirectToAction("Edit", "Editor"); // Goes back to the editing page
}
I'm trying to cause the download at the place in the code I specified, but I'm not sure how to cause it. Usually you return it somehow to the view, whereas here I'm calling an object, I think, but I'm really fuzzy on how that all works. Regardless, something I'm doing isn't working. Any ideas on what that is?
You can't return the ViewResult RedirectToAction("Edit", "Editor") and in the same response a FileResult File(PdfUrl, "application/pdf")
To accomplish your task you could follow this scenario:
click on button create pdf
call the RedirectToAction("Edit", "Editor");
in this case at the end of the view, add a javascript call to the action method returning the FileResult
once the page is rendered, the download will start automatically
Related
In our AngularJS / ASP.Net WebAPI application, we need to generate a PDF file when clicking a button. The button re-directs the user to the following URL on a new browser page:
xyz.com/api/getpdf?Token=f3Ttkwf5XyvvZwcOZpEz
getpdf is a API that returns the PDF file or an HTML error code if an error occurs.
The problem is that the PDF can take up to 30 seconds to generate. How can I display some HTML on the page (informing user to wait) before I send the file? The HTML should also be returned in the same API call. I've read that it is possible to have multiple responses on one request. But how do I do this?
This is the simplified code in the controller to return the PDF:
[HttpGet]
public HttpResponseMessage GetPDF(string Token)
{
HttpResponseMessage resp = null;
try
{
// Generate PDF
Byte[] lPDF = GetPDF(Token);
// Return PDF in response
resp = Request.CreateResponse(HttpStatusCode.OK);
resp.Content = new ByteArrayContent(lPDF);
resp.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("inline");
resp.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
resp.Content.Headers.ContentDisposition.FileName = "PDF-File.pdf";
resp.Content.Headers.ContentLength = lPDF.Length;
return resp;
}
catch (Exception ex)
{
Toolkit.LogError("GetPDF", ex);
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
You can create an overlay on the previous page.
For example you have a button who call your controller's method, on click on this button display your overlay with the message 'Loading'.
When the document will be generated, the page will be redirected and the overlay will disappear.
Example with JQuery
$("#generate").click(function(){
$(".overlay").show();
setTimeout(function(){window.location.href="http://stackoverflow.com"},2000);
});
.overlay{
display:none;
position:fixed;
top:0;
left:0;
background-color:rgba(0,0,0,0.5);
width:100%;
height:100%;
color:white;
text-align:center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="overlay">Loading...</div>
<button id="generate">Generate</button>
Here the setTimeout method is just used for simulate a time of response.
If I understand you correctly you may generate pdf-file in two steps.
First step is to ask web server to generate pdf-file. (POST-request)
Second step is to ask web server if pdf-file is already generated. (GET-request or SignalR)
I am using Spring MVC. In my jsp page i have table which inline editable and with every table i have attached one button during edit (when you want to edit then just click on edit button in the dropdown, immediately that row will become editable and edit button will be visible beside that row) so when i will click it, immediately it should save the data of that row to database.
I can do that by providing a link (using tag) with button so when i will click this link, it will match with #RequestMapping in the Controller and from there i will save the data of whole table into database. Now again i have to come back to my previous page so again it will load whole page from database which is very costly.
Can some help me so that only that row id should go to controller and from there it should save into database and i don't have to reload the page again.
You should send your request via Ajax. The page will never reload, and you can choose to reload only a page segment. The easiest way is to use jQuery, bind a method to your links, something like
$("a.saveToDB").click(function(){
var link = $(this);
$.ajax({
url: link.attr("href"),
dataType: "text",
success: function(text) {
// show success message to the user
},
error: function(xhr) {
// show error message to the user
}});
// prevent links default action
return false;
});
The controller method can return a String, e.g.
#RequestMapping(value="/save", method=RequestMethod.GET)
public #ResponseBody String saveToDB() {
// save to DB
return "success";
}
I have a dropdown list that pulls data from template table. I have an Add button to insert new template. Add button will brings up jQuery popup to insert new values. There will be a save button to save the new data. On_Save_Click I enter the new data and close the popup.
Here is the proplem:
When I refresh the page, the page entering the values again. So, I get duplicate entries!
Question:
How can I avoid this issue? I check out Satckoverflow and Google, both they suggest to redirect to another page. I don't want to redirect the user to another page. How can I use the same form to avoid this issue? Please help.
You can use viewstate or session to indicate if data already inserted (button pressed).
Something like this:
private void OnbuttonAdd_click()
{
if(ViewState["DataInserted"] != "1")
{
...
// Add new entry...
...
if(data inserted successfully)
{
ViewState["DataInserted"] = "1";
}
}
}
Edit:
public bool DataInserted
{
get
{
if (HttpContext.Current.Session["DataInserted"] == null)
{
HttpContext.Current.Session["DataInserted"] = false;
}
bool? dataInserted = HttpContext.Current.Session["DataInserted"] as bool?;
return dataInserted.Value;
}
set
{
HttpContext.Current.Session["DataInserted"] = value;
}
}
...
private void OnbuttonAdd_click()
{
if(!DataInserted)
{
...
// Add new entry...
...
if(data inserted successfully)
{
DataInserted = true;
}
}
}
The simplest way is to use a post/redirect/get pattern.
Basically, the refresh action for page build with post requires to repost the data. Using this pattern, you will reload the whole page.
With ASP.Net, you have a simple alternative, use an UpdatePanel. This will refresh only part of the page using AJAX. As the page itself is still the result of a GET request, you can refresh the page. And as you use ASP.Net, it's quite easy to integrate.
Finally, you can use a home made AJAX refresh. A combination of jQuery, KnockOut and rest services (for example), can help you to avoid refreshing the full page in benefits of an ajax call.
There is some experience:
Disable Submit button on click (in client side by JavaScript).
change Session['issaved'] = true on save operation at server side and change it on new action to false.
use view state for pass parameters like RecordId (instead of QueryString) to clear on refresh page. i always pass parameter's with Session to new page, then at page load set
ViewState['aaa']=Session['aaa'] and clear Sessions.
...I hope be useful...
Do this it is very easy and effective
Intead of giving IsPostBack in the page load(),please provide inside the button click (To send or insert data)
Call the same page again after reseting all input values
protected void Btn_Reg_Click1(object sender, EventArgs e)
{
try
{
if (IsPostBack)
{
Registration_Save();
Send_Mail();
txtEmail.Text = "";
txtname.Text = "";
Response.Redirect("~/index.aspx");
}
}
catch (Exception) { }
}
You won't see any server messages after refreshing the page..
I'm using the jQuery NotifyBar quite nicely in an Index view to display business rule errors when a user e.g. clicks a delete link for an item than cannot be deleted. However, if the user adds a new item, they are redirected to the Create view. If the new item is successfully created, the Create action redirects back to the Index view.
My quandary is that I need (have been told) to show a success notification in the above scenario. Previously, to request a notification while remaining on the same view, I was using return JavaScript() for an action result, but when I use return RedirectAction() for the action result, I'm left with nowhere to put the return JavaScript().
The way I see this is that I need to:
a) include information in the return RedirectAction() that tells the 'destination' view to show the notification, or
b) invoke the notification in the 'source' view, instead of the return RedirectAction(), and tell it that when it closes/is closed, to perform the redirect to the 'destination' view.
I have no idea where to begin deciding between these two opetions, nor how to even begin researching how to implement either. All advicem and pointers to advice will be most appreciated.
I like option A the best. You could easily include a querystring value with the return url and have a javascript function waiting on the return page that looks for the querystring value... if present, show notification bar.
Submit action on controller:
public ActionResult Submit(ValueModel valueModel) {
//TODO: Save model to repository
//include something in the route values to act as a querystring flag.
//here, I use "success".
return RedirectToAction("Action", "Controller", new { success = 1 });
}
View action on controller:
public ViewResult Index() {
//TODO: do stuff
return View();
}
Index.aspx:
...
<div class='notificationBar'></div>
<script type="text/javascript">
$(document).ready(function() {
if(window.location.search.substring(1).indexOf("success")) {
//set notification bar here
}
});
</script>
...
I am trying to use link to submit a form using the following code:
function deleteItem(formId) {
// submit the form
$("#" + formId).submit();
}
Basically I have a grid and it displays a number of items. Each row has a delete button which deletes the item. I then fire the following function to remove the item from the grid.
function onItemDeleted(name) {
$("#" + name).remove();
}
It works fine when I use a submit button but when I use action link the JavaScript from the controller action is returned as string and not executed.
public JavaScriptResult DeleteItem(string name)
{
var isAjaxRequest = Request.IsAjaxRequest();
_stockService.Delete(name);
var script = String.Format("onItemDeleted('{0}')", name);
return JavaScript(script);
}
And here is the HTML code:
<td>
<% using (Ajax.BeginForm("DeleteItem",null, new AjaxOptions() { LoadingElementId = "divLoading", UpdateTargetId = "divDisplay" },new { id="form_"+stock.Name }))
{ %>
<%=Html.Hidden("name", stock.Name)%>
<a id="link_delete" href="#" onclick="deleteItem('form_ABC')">Delete</a>
<% } %>
</td>
My theory is that submit button does alter the response while the action link simply returns whatever is returned from the controller's action. This means when using submit the JavaScript is added to the response and then executed while in case of action link it is simply returned as string.
If that is the case how can someone use action links instead of submit buttons.
UPDATE:
Seems like I need to perform something extra to make the action link to work since it does not fire the onsubmit event.
http://www.devproconnections.com/article/aspnet22/posting-forms-the-ajax-way-in-asp-net-mvc.aspx
My guess is the MS Ajax form knows how to handle a JavaScriptResponse and execute the code whereas your plain old Action link, with no relationship to the AjaxForm, does not. I'm pretty sure the MS ajax library essentially eval()s the response when it sees the content type of javascript being sent back.
Since you have no callback in your deleteItem() method there is no place for the script to go. To fix you'll have to manually eval() the string sent back which is considered a bad practice.
Now I'm not familiar with the MS Ajax library to be certain of any of this but what your doing is possible. I'd do things differently but don't want to answer with a "my way is better" approach ( especially because your blog has helped me before ) but I'd like to show this can be easier.
I'd ditch the form entirely and use unobtrusive javascript to get the behavior you want. IN psuedo jqueryish ( don't know ms ajax ) code:
function bindMyGrid() {
$('.myDeleteLink').onclicksyntax( function() {
//find the td in the row which contains the name and get the text
var nameTdCell = this.findThisCellSibling();
//do an ajax request to your controller
ajax('myUrl/' + nameTdCell.text(), function onSuccessCallback() {
//get the tr row of the name cell and remove it
nameTdCell.parent().remove();
});
});
}
This also gains the benefit of not returning javascript from your controller which some consider breaking the MVC pattern and seperation of concerns. Hope my psuedo code helps.
Try without the UpdateTargetId property in the AjaxOptions (don't specify it)
new AjaxOptions() { LoadingElementId = "divLoading" }
What about just change look of a standard or using some css class? It'll look like a link and you'll avoid some problems you get with anchors - user will be able to click on it by a mouse wheel and open that link in a new tab/window.