Using MVC3 AJAX call to ActionResults fails when returning a File - ajax

I have an action result that returns a created File object (simplified for display!):
public ActionResult Create(int recID, int templateID)
{
//Get dbRecord from recID
DBRecord dbRecord = db.dbRecord.Find(recID);
//Get Template from templateID
Template template = db.Templates.Find(templateID);
if (template == null) throw new FileLoadException(string.Format("No database record found for template reference {0}",templateID));
//set fileOutput details
WordFile fileOutput = new WordFile(dbRecord, Server.MapPath("~/Documents/"),template);
//Create XML object for Template
XmlDocument xDoc = new XmlDocument();
//Save resulting document
xDoc.InnerXml = mergeData(template,dbRecord);
xDoc.Save(fileOutput.fullName);
if (!System.IO.File.Exists(fileOutput.fullName)) throw new FileNotFoundException(string.Format("File {0} could not be created", fileOutput.fileName));
//Return saved document
return File(fileOutput.fullName, "application/doc", fileOutput.fileName);
}
If I call this via an HTML RouteLink
<%: Html.RouteLink("Generate via HTML
, "GenerateDocument"
, new RouteValueDictionary(
new {
controller = "Template"
, action = "Create"
, recID = 1
,templateID = 1
}
)
)%>
Edit 1: It works fine, the document is generated and the user is prompted to open, save, etc. However, if I call it via an AJAX RouteLink, the code steps through, the document is created on the server, but the user is not prompted to open or save.
<%: Ajax.RouteLink("Generate via AJAX"
, "GenerateDocument"
, new RouteValueDictionary(
new {
controller = "Template"
, action = "Create"
, recID = 1
, templateID = 1 }
)
, new AjaxOptions
{
HttpMethod="POST"
, LoadingElementId="Refresh"
})%>
Is there an inherent restriction that prevents AJAX returning a file type or have I missed the right search combination to find an obvious answer?

I'm not sure I understand what you're talking about. Are you trying to return the file contents to a javascript variable? That is the only thing I can imagine an "AJAX RouteLink" would be good for. If you want it to prompt the user to open or save, use a normal link. If you're trying to embed the file in the page, don't return the file, return the html that you would use to do the embedding.

Related

How to create a string for #Html.ActionLink that concatenates an integer from the Model and a string

I'm using VS2015, MVC 5 design model. Creating a link that says "View" to open a PDF file in a new browser tab. It works fine, but the document sub-directory is hard-coded in the controller. I need to pass the document sub-directory + filename to the controller. The document sub-directory is the same as the Model.id . I'm having a difficult time converting the Model.id to a string and concatenating with the filename.
The following code in the view works fine with the hard-coded sub-directory in the controller
<td>#Html.ActionLink("View", "ViewAttachedDoc", "Documents", new { filename = item.filename}, new { target = "_blank" })</td>
But this code does not work
<td>#Html.ActionLink("View", "ViewAttachedDoc", "Documents", new { filename = Convert.ToString(Model.id) + "\" + item.filename }, new { target = "_blank" })</td>
The controller action is:
public FileResult ViewAttachedDoc(string filename)
{
string DocPath = ConfigurationManager.AppSettings["DocPath"];
string path = Path.Combine(DocPath, filename);
return File(path, "application/pdf");
}
TIA,
Tracy
The issue is likely from trying to pass a backslash through the URL. This link of a similar question has that same problem and their solution was to use HttpUtility.UrlEncode(value); and HttpUtility.UrlDecode(value);. Otherwise if that still doesn't work, what error are you getting?
P.S. C# automatically converts / -> \ for retrieving files.

how do i call the controller in kendo ui grid mvc

How do i call the controller in mvc kendoui grid
here is the code:
but not working:
.ClientTemplate(string.Format("<a class=\"modal\" rel=\"/address/#= Id #/map\" close=\"{0}\" title=\"{1}\"><img src=\"/content/images/ico_edit_16.png\" /></a>",
T("Common.Close").Text,
T("Address.MapAddress").Text)
here is the controller:
public ActionResult AddressMap( int accountId)
{
//load default accounts
var listModel = new AddressListModel();
//{
// AccountId = accountId,
// GridPageSize = _commonSettings.GridPageSize,
//};
//listModel.Addresses = new List<AddressModel>();
return View(listModel);
}
Using your existing code:
.ClientTemplate(string.Format("<a class=\"modal\" rel=\"/address/#= Id #/map\"
close=\"{0}\" title=\"{1}\"><img src=\"/content/images/ico_edit_16.png\" /></a>",
T("Common.Close").Text,
T("Address.MapAddress").Text)
You will see you are setting the a tag's rel setting rather than the href property.
so all you should need to do is change the tag so it is more like this:
(string.Format("<a class='k-link' href='{2}' close='{0}' title='{1}'><img src='/content/images/ico_edit_16.png' />#=Id#</a>",
T("Common.Close").Text,
T("Address.MapAddress").Text, #Url.Action("AddressMap", "{Your Controller}", new {accountId= "#=Id#"})))
All I have done is passed the url into the string formatter and then bound the accountId to the Id that is passed into the row from the dataItem.
I have also added the Id to the link so you can see what is being presented.
If this isn't what you wanted then let me know what you actually need and I will amend the answer accordingly

Create a custum Sharepoint list and add items problems

Im new to SharePoint programming, or programming at all for that case. My problem is that I get a error-message when I try to go to my sharepoint site after I debugged this oode (it worked fine in visual studio). I don't know whats wrong? The error message i get is:
"Server Error in '/' Application.
0x80004005Updates are currently disallowed on GET requests. To allow updates on a GET, set the 'AllowUnsafeUpdates' property on SPWeb."
I've read that you got to allow safe updates? but is that the case? I think maybe it is my code that is not right... Please help :)
// choose your site
SPSite site = new SPSite("http://....");
SPWeb web = SPContext.Current.Web;
//Add a list to the choosen site
SPListCollection lists = web.Lists;
// create new Generic list called "OrdersList"
lists.Add("OrdersTest", "All of my testorders will be here", SPListTemplateType.GenericList);
//Add the new list to the website
SPList newList = web.Lists["OrdersTest"];
// create Number type new column called "OrderID"
newList.Fields.Add("OrderID", SPFieldType.Number, true);
// create Number type new column called "OrderNumber"
newList.Fields.Add("OrderNumber", SPFieldType.Number, true);
// create Text type new column called "OrderProducts"
newList.Fields.Add("OrderProducts", SPFieldType.Text, true);
newList.Update();
//Create a object to that list
SPListItem newOrder = newList.AddItem();
//Add a new item
newOrder["OrderID"] = 1;
newOrder["OrderNumber"] = 1;
newOrder["OrderProducts"] = "Icecream";
newOrder.Update();
// create new Generic list called "ProductTest"
lists.Add("ProductTest", "All of my testproducts will be here", SPListTemplateType.GenericList);
//Add the new list to the website
SPList newList2 = web.Lists["ProductTest"];
// create Number type new column called "ProductID"
newList2.Fields.Add("ProductID", SPFieldType.Number, true);
// create Text type new column called "ProductName"
newList2.Fields.Add("ProductName", SPFieldType.Text, true);
// create Number type new column called "OrderID"
newList2.Fields.Add("ProductPrice", SPFieldType.Number, true);
//Create a object to that list
SPListItem nyProduct = newList2.AddItem();
//Add a new item
nyProduct["ProductID"] = 1;
nyProduct["ProductName"] = "Icecream";
nyProduct["ProductPrice"] = 13;
nyProduct.Update();
You need to set AllowUnsafeUpdates to true for updating data in get requests.
using(SPSite site = new SPSite("http://...."))
using (SPWeb web = site.OpenWeb())
{
bool b = web.AllowUnsafeUpdates;
web.AllowUnsafeUpdates = true;
try
{
//your code
//create and update list
}
catch (Exception ex)
{
//handles errors
}
finally
{
web.AllowUnsafeUpdates = b;
}
}

MVC3 Razor Passing ViewModel to Conroller List<> is null

I have a MVC 3 applicaiton in which I pass a vewmodel from the controller to the view. The vewmodel contains a couple of List<> properties.
public ActionResult MainView()
{
var model = GetViewModel();
return View("SignificantEventsView", model);
}
private SignificantEventsViewModel GetViewModel()
{
var viewModel = new SignificantEventsViewModel();
List<County_Codes> countyCodes = GetCountyCodeList();
List<String> stateNames = countyCodes.OrderBy(o=>o.County_st).Select(o => o.County_st ).Distinct().ToList();
viewModel.selectedState = stateNames.FirstOrDefault();
viewModel.CountyCodesList = countyCodes;
viewModel.StateNames = stateNames;
viewModel.SelectedCounties = new String[]{};
viewModel.SelectedCountyCodes = new String[] { };
viewModel.UnSelectedCounties = new String[] { };
viewModel.UnSelectedCountyCodes = new String[]{};
return viewModel;
}
The View looks like this:
#model ServicingPortal.ViewModels.SignificantEventsViewModel
#{
ViewBag.Title = "Significant Events";
}
<h2>SignificantEvents</h2>
#using (Html.BeginForm("RefreshCounties", "SignificantEvents", FormMethod.Post, new { id = "significantEventsForm", Model }))
{
<fieldset>
<span class="SpanTextboxEdit">
#Html.Label("states", "States")
<br />
<br />
#Html.DropDownListFor(o => #Model.selectedState
, new SelectList(Model.StateNames)
, new { id = "stateDropDown", onchange = "submit()", name = "test" })
</span>
</fieldset>
...
}
When the StateDropdownList is changed the veiwmodel is passed back to the controller, but the countyCodes list is always null.
I tried adding #Html.HiddenFor(o => #Model.CountyCodesList) in the view, but it still returns null. The only values that don't seem to be null are the primitive types such as String or String[]. Even the List stateNames is null.
I don't want to rebuild the county code list on each post back because there is substantial overhead involved. I have to create the list from all active loans in the database, of which there are thousands.
How can I get a List<> to persist from the view to the controller?
I should explain what I'm trying to acheive here.
I have a dropdown and a multiselect list box. The dropdown contains states and the listbox contains counties filtered by the selected state.
I need to filter the listbox contents when the selected state changes.
It would make sense to perform this task on the client side, but I have not found a good solution.
I will admit my javascript skills are quite limited.
All the solutions I researched one way or another involved filtering the county list on the server side.
I can accomplish this on the server side easy enough, but I thought that since I have already built the list, why not keep it intact instead of going to the backend each time.
The short answer is that you can't really do what you're trying to do. You're kind of trying to solve the wrong problem. You should look at using caching on the server side to prevent going back to the database to construct the county list every time.
I solved this by using TempData. On the postback action I can get the County List from temp data and set the ViewModel CountyCodeList to this value.

Getting a TinyMCE dropdown image list in an MVC3 application

I am trying to get an MVC3 app to feed TinyMCE with an external image list javascript file. I have TinyMCE set up so that if I use a static image list file, I get the image list, so I know that part works. But since I need to dynamically create the image list per user, I need something more flexible than a static file. It is down to providing the javascript from the following controller action:
public JavaScriptResult TinyMCEImageList(int id)
{
ListHelper lh = new ListHelper();
string js = "var tinyMCEImageList = new Array(\r\n" + "// Name, URL\r\n";
Dictionary<string, string> dict = lh.GetPetImageURLs(id);
int i = dict.Count();
foreach (var item in dict)
{
js += "['" + item.Key + "', '" + item.Value + "']";
if (i > 1)
{
js += ",\r\n";
}
i--;
}
js += "\r\n);";
return JavaScript(js);
}
The ListHelper.GetPetImageURLs() returns a dictionary object, which is simply a convenient way to hold the caption and URL of each image. When I call this controller from the browser, with the appropriate id parameter, I get what I would think is a workable JS file. In fact, such results are what I used to create the static file I used to test the TinyMCE image list setup, and that got me an actual dropdown image list.
Here is my TinyMCE setup. This is inside the view containing a TinyMCE instance:
tinyMCE.init({
mode: "textareas",
theme: "advanced",
plugins: "lists,pagebreak,style,table,inlinepopups,advhr,advimage,preview,searchreplace,print,paste,visualchars,nonbreaking",
theme_advanced_buttons1: "newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,formatselect,fontselect,fontsizeselect",
theme_advanced_buttons2: "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,forecolor,backcolor,image",
theme_advanced_buttons3: "tablecontrols,|,visualaid,|,sub,sup,|,advhr,|,preview,print,|,visualchars,nonbreaking,template,blockquote,pagebreak",
theme_advanced_toolbar_location: "top",
theme_advanced_toolbar_align: "left",
theme_advanced_statusbar_location: "bottom",
theme_advanced_resizing: true,
external_image_list_url: "/InstructionDocument/TinyMCEImageList/#ViewBag.PetID"
});
The #ViewBag.PetID is being used elsewhere, so I know it is valid. Even if I hardcode this value, or specifically point to the controller action, I still do not get a dropdown list of images. I'm sure there's something simple I'm missing; can someone point out to me what it is (or at least give me some reasonable guidance)?
[EDIT]
The output from the TinyMCEImageList() action follows:
var tinyMCEImageList = new Array(
// Name, URL
['System Guinea Pig - 4', 'http://www.remindapet.com/Content/images/galler/1_4_970BB64F7C1A4375AF5722B8A62C8708.jpg'],
['System Guinea Pig - 5', 'http://www.remindapet.com/Content/images/gallery/1_4_CBA0D3DDBBED41C583A6E6C46FC9DADF.jpg']
);
Also, here are the headers for the above javascript return from the action:
Server ASP.NET Development Server/10.0.0.0
Date Fri, 23 Dec 2011 00:14:31 GMT
X-AspNet-Version 4.0.30319
X-AspNetMvc-Version 3.0
Cache-Control private
Content-Type application/x-javascript; charset=utf-8
Content-Length 292
Connection Close
So, the action really is returning a JavascriptResult. I just haven't been able to come up with how to get TinyMCE to see it.
Thanks!
Instead of rendering an javascriptresult, create a js file while you are rendering the page.
Controller
public ActionResult Index()
{
MakeJSFile();
return View();
}
MakeJSFile() function will create the jsfile we need, then the page will be rendered.
MakeJSFile() function
public void MakeJSFile()
{
#region declare
var imgPath = Server.MapPath("~/Content/images/gallery/");
var jsPath = Server.MapPath("~/Scripts/image_list.js");
List<string> fileList = populateList(imgPath, ".jpg");
#endregion
#region build jsfile
string content = "var tinyMCEImageList = new Array(";
foreach (var item in fileList)
{
content += "[\"" + item + "\", \"/Content/img/" + item + "\"]";
if (item != fileList.Last())
content += ",";
}
content += ");";
#endregion
#region create file
StreamWriter sw = new StreamWriter(jsPath, false);
sw.Write(content);
sw.Close();
#endregion
}
First declare the path for the directory where the images is, and also the path of the jsfile. Then create a list that contains the filenames and populate it(with populateList function).
Then create a string to build the jsfile.
After that create the file in you server.
You only need to do one more thing, create the populateList function.
PopulateList function
public List<string> populateList(string path, params string[] extensions)
{
List<string> list = new List<string>();
FileInfo fi = new FileInfo(path);
DirectoryInfo di = fi.Directory;
FileSystemInfo[] fsi = di.GetFiles();
foreach (FileSystemInfo info in fsi)
foreach (var ext in extensions)
if (info.Extension == ext)
list.Add(info.Name);
return list;
}
This function requires the path to the directory and file extensions.
If you want a specific list, just change this function.
One more thing, don't forget to change the value of external_image_list_url
tinyMCE.init({
...
external_image_list_url: "/Scripts/image_list.js"
});

Resources