I have a partial view that has an Ajax.ActionLink that when clicked should replace the view (target DIV) with another partial view that allows a user to upload an image. I have several of these on the page that work, I just can't seem to figure out why I keep getting a 404 for this particular one.
Here's what I have:
MyProfile.cshtml holds all the partials (tenant-reference-photos is update target DIV):
<div class="row-fluid">
<div class="span6 well-border" id="tenant-reference-photos">
#Html.Partial("~/Views/Tenants/_TenantReferencePhotosPartial.cshtml", Model)
</div>
<div class="span6 well-border" id="tenant-viewings">
#Html.Partial("~/Views/Tenants/_TenantViewingsPartial.cshtml", Model)
</div>
</div>
_TenantReferencePhotosPartial.cshtml (ActionLink giving 404 here):
<div class="row-fluid">
#if (Model.ReferencePhotos == null)
{
<h3>You haven't uploaded any references!
#Ajax.ActionLink("Upload now?", // on click 404 returned in developer tools in Chrome
"UploadReference",
new AjaxOptions
{
UpdateTargetId = "tenant-reference-photos",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
LoadingElementId = "ajax-loader"
})
</h3>
}
</div>
Code below returns the above partial:
[HttpGet]
public ActionResult TenantReferencePhotos()
{
var currentTenant = tenantRepository.GetLoggedInTenant();
return PartialView("_TenantReferencePhotosPartial", currentTenant.ReferencePhotos.ToList());
}
The following ActionResult is what's not being invoked in the Ajax.ActionLink and giving the 404 error:
[HttpPost]
public ActionResult UploadReference(HttpPostedFileBase file, Tenant tenant)
{
if (file != null)
{
if (file.ContentLength > 10240)
{
ModelState.AddModelError("file", "The size of the file should not exceed 10 KB");
return View();
}
var supportedTypes = new[] { "jpg", "jpeg", "png", "JPG", "JPEG", "PNG" };
var fileExt = System.IO.Path.GetExtension(file.FileName).Substring(1);
if (!supportedTypes.Contains(fileExt))
{
ModelState.AddModelError("photo", "Invalid type. Only the following types (jpg, jpeg, png) are supported.");
return View();
}
using (var db = new LetLordContext())
{
var reference = db.Image.Create<ReferencePhoto>();
// Convert HttpPostedFileBase to byte array
MemoryStream target = new MemoryStream();
file.InputStream.CopyTo(target);
byte[] photo = target.ToArray();
reference.File = photo;
reference.Format = fileExt;
reference.DateUploaded = DateTime.Now.Date;
reference.Description = "";
reference.Name = "";
db.Image.Add(reference);
db.SaveChanges();
return PartialView("_TenantReferencePhotosPartial", file);
}
}
else
{
return View();
}
}
For completeness, here's the partial view where an image can be uploaded:
<div>
#using (Ajax.BeginForm("UploadReference", "Tenants", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "tenant-reference-photos"
}))
{
#Html.ValidationSummary(true)
#Html.AntiForgeryToken()
<div>
Select a file:
<input type="file" name="file" />
<input type="submit" value="UploadReference" />
</div>
}
</div>
All scripts in MyProfile.cshtml are referenced. Does unobtrusive-ajax.js need to be included as a script in all partial views even if it's referenced in Layout.cshtml and/or MyProfile.cshmtml?
Can anyone spot why I'm getting the error above?
In your code UploadReference is decorated with HttpPost attribute so it is accessible only when you make POST. In your view you have configured HttpMethod to GET. When you change it to POST it should work:
#Ajax.ActionLink("Upload now?",
"UploadReference",
new AjaxOptions
{
UpdateTargetId = "tenant-reference-photos",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
LoadingElementId = "ajax-loader"
})
Related
at this moment I am uploading any file and saving it. What I want is to generate an error message if the user does not select any file and press the upload button, but at this moment the only thing it does is redirect to another view whether or not it has selected files. I would like to know if there is another better way to generate the upload of these files
this is my controller
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
[HttpPost]
public void Upload(HttpPostedFileBase file)
{
string file = (file.FileName).ToLower();
try
{
file.SaveAs(Server.MapPath("~/Uploads/" + file));
}
catch (Exception e)
{
ViewBag.UploadError = "Upload file error";
}
}
}
this is the view:
#{
ViewBag.Title = "Home";
}
#using (Html.BeginForm("Transformation", "Xml", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="col-md-12 offset-md-5">
<div class="custom-file col col-lg-2">
<input type="file" name="file" class="custom-file-input" id="inputGroupFile01" aria-describedby="inputGroupFileAddon01">
<label class="custom-file-label" for="inputGroupFile01">Choose file</label>
</div>
<div class="col-5">
<button class="btn btn-success col-md-4 mt-2" type="submit">Upload file</button>
</div>
</div>
//Message Error
<div class="alert alert-danger" role="alert">
<p class="text-danger">
#ViewBag.UploadError
</p>
</div>
}
#Thomas Caycedo Martinez, I believe you can simply modify your controller method like below.
If error, return to the same view with an error message.
Your view remains unchanged.
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
if (file != null)
{
string fileName = (file.FileName).ToLower();
try
{
file.SaveAs(Server.MapPath("~/Uploads/" + fileName));
}
catch (Exception e)
{
ViewBag.UploadError = "Upload file error";
return View("Index");
}
}
else {
ViewBag.UploadError = "Upload file error";
return View("Index");
}
return View();
}
write the action and controller correctly
#using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
and check file in action
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
if( file != null && file.Length > 0)
{
string file = (file.FileName).ToLower();
try
{
file.SaveAs(Server.MapPath("~/Uploads/" + file));
}
catch (Exception e)
{
ViewBag.UploadError = "Upload file error";
}
return View("Index");
}
else
{
//do something
return View("Index");
}
}
You can use JavaScript/JQuery to check for the same..
$(function(){
$("#btnSubmit").on("click",function(){
if($("#inputGroupFile01").val()==""){
alert("Please select a file")
return false;
}
})
})
where btnSubmit is the id for the submit button
I have a mvc web application which contains chart and table data.
Scenario:
I am populating data in the Index method and return the model to view for binding. In view, table bind with the data. I also want to bind the chart with same data. Chart bind with child action. I set TempData[] object in the Index method and retrieve the same in Chart action.
Code:
Controller
public ActionResult Index()
{
var data = new List<MyModel>()
{
new MyModel { Text = "Name1", Value = 123 },
new MyModel { Text = "Name2", Value = 24 },
};
TempData["data"] = data;
return View(data);
}
public FileContentResult Chart()
{
List<MyModel> data = TempData["data"] as List<MyModel>;
var chart = new Chart
{
Width = 300,
Height = 450,
};
if (data != null)
{
chart.ChartAreas.Add("");
chart.Series.Add("");
chart.Series[0].ChartType = SeriesChartType.Column;
foreach (var q in data)
{
chart.Series[0].Points.AddXY(q.Text, Convert.ToDouble(q.Value));
}
}
using (var chartimage = new MemoryStream())
{
chart.SaveImage(chartimage, ChartImageFormat.Png);
return File(chartimage.GetBuffer(), #"image/png");
}
}
public async Task<ActionResult> Export()
{
var converter = new HtmlToPdf();
var baseAddress = new Uri("http://localhost:4545/");
var cookieContainer = new System.Net.CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (HttpClient client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var result = await client.GetAsync("/Home/Index");
var htmlString = await result.Content.ReadAsStringAsync();
var doc = converter.ConvertHtmlString(htmlString, baseAddress.ToString());
doc.Save(System.Web.HttpContext.Current.Response, true, "test.pdf");
doc.Close();
}
return null;
}
View:
<div class="row">
#using (Html.BeginForm("Index", "Home", FormMethod.Get))
{
<div class="col-md-12">
<table>
#{
foreach (var item in Model)
{
<tr>
<td>
#item.Text
</td>
<td>
#item.Value
</td>
</tr>
}
}
</table>
</div>
<div class="col-md-12">
<img src="#Url.Action("Chart")" />
<input type="submit" value="Filter" />
</div>
}
<div class="col-md-12">
#using (Html.BeginForm("Export", "Home", FormMethod.Post))
{
<input type="submit" value="Export" />
}
</div>
Problem:
Here i am using SelectPdf to export the web page to pdf. When i click Export button, it takes the content of web page so that again Index and Chart rendered. But here i didn't get tempdata[] values. It shows as null. So chart data not getting in pdf
Please help me to solve this issue
Please Note :
TempData in ASP.NET MVC can be used to store temporary data which can be used in the subsequent request. TempData will be cleared out after the completion of a subsequent request.
So to retain the values of TempData in subsequent request you need to call either TempData.Keep() or Peek("tempdata name").
Keep Syntax:
String data = TempData["myData"] as string;
TempData.Keep();
Peek Syntax:
String data = TempData.Peek("myData");
Try this
List<MyModel> data = (List<MyModel>)TempData.Peek("data");
I'm new in asp net and I have a List of images and I want to send through javascript to the controller.
I'm using FileList and here is an example.
.Create.cshtml
<div class="form-group">
#Html.LabelFor(model => model.description, "Escolha as Imagens", htmlAttributes: new { #class = "control-label col-md-2" })
<input id="files" type="file" name="files[]" />
<br>
<div id="preview"></div>
</div>
#section Scripts{
<script type="text/javascript">
function handleFileSelect(evt) {
var files = evt.target.files;
var f = files[0];
//kiem tra co fai file anh
if (f.type.match('image.*')) {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = [
'<img class="thumb" src="', e.target.result, '" title="', escape(theFile.name),
'"/><span class="remove_img_preview"></span>'
].join('');
document.getElementById('preview').insertBefore(span, null);
};
})(f);
reader.readAsDataURL(f);
}
}
$('#files').change(function(evt) {
handleFileSelect(evt);
});
$('#preview').on('click',
'.remove_img_preview',
function() {
$(this).parent('span').remove();
});
$('#btnSave').click(function() {
$.ajax({
url: '/Dishes/Create',
data: { files: files },
type: "POST",
cache: false,
datatype: "html",
success: function(data) {
console.log(data);
},
error: function(jqXhr, textStatus, errorThrown) {
//do your own thing
alert("fail");
}
});
});
</script>
}
</fieldset>
<div class="form-group">
<div class="footer text-center">
<button class="btn btn-fill btn-info" name="btnSave" id="btnSave">Inserir novo</button>
</div>
</div>
Controller.cs
public ActionResult Create()
{
ViewBag.idTypeDish = new SelectList(db.TypeDish, "idTypeDish", "name");
return View();
}
// POST: Dishes/Create
[HttpPost]
public ActionResult Create(IEnumerable<HttpPostedFileBase> files)
{
return View();
}
In the controller, files are always null.
I'm using that example, http://jsfiddle.net/Lwczca6p/1/, I just adapt to my project.
You've got a few problems. First, you're trying to use files with your AJAX, but that variable was defined in the scope of another function (i.e. you don't have access to it here). Second, when using jQuery's $.ajax to do a file upload, you need to set the processData option to false. Here's how I would handle it:
$('#MyForm').on('submit', function (e) {
e.preventDefault();
var formData = new FormData(this); // `this` is the form instance
$.ajax({
url: '/path/to/handler',
type: 'POST',
data: formData,
processData: false,
contentType: 'multipart/form-data',
success: function (data, textStatus, jqXHR) {
// do something on success
},
error: function (jqXHR, textStatus, errorThrown) {
// do something on error
}
});
});
Try this, first create a model for your view:
public class FileModel
{
[Required(ErrorMessage ="Please select file.")]
[Display(Name ="Browse File")]
public HttpPostedFileBase[] files { get; set; }
}
Then, edit your view to this (to enable upload many files you need to put multiple="multiple" in your element):
#model Models.FileModel
#using (Html.BeginForm("Create", "YourController", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.files, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.files, "", new { #type = "file", #multiple = "multiple" })
#Html.ValidationMessageFor(model => model.files, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Upload" class="btn btn-primary" />
</div>
</div>
</div>
}
This is a part of my view code for Index action of Manage Controller.
<div class="mngimg">
#using (Html.BeginForm("UploadPhoto", "Manage", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="btn btn-default browseimg">
<input type="file" name="file" id="files" onchange="this.form.submit()" />
</div>
<div class="btn btn-default browseimg">
#Html.ActionLink("Remove Photo", "RemovePhoto", "Manage")
</div>
}
</div>
</div>
}
</dd>
<dt>Password:</dt>
<dd>
[
#if (Model.HasPassword) <!-- Here is my error. The Model is null -->
{
#Html.ActionLink("Change your password", "ChangePassword")
}
else
{
#Html.ActionLink("Create", "SetPassword")
}
]
</dd>
Whenever I open this page and click "Remove Photo" I keep getting an error saying that An exception of type 'System.NullReferenceException' occurred in App_Web_ckoryptg.dll but was not handled in user code. I tried debugging, but I am unable to figure out why my Model.HasPassword is becoming null. Here is my RemovePhoto Action from Manage Controller.
[HttpPost]
public async Task<ActionResult> UploadPhoto(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var user = await GetCurrentUserAsync();
var userId = user.Id;
var fileExt = Path.GetExtension(file.FileName);
var fnm = userId + ".png";
if (fileExt.ToLower().EndsWith(".png") || fileExt.ToLower().EndsWith(".jpg") || fileExt.ToLower().EndsWith(".gif"))// Important for security if saving in webroot
{
var filePath = HostingEnvironment.MapPath("~/Content/Images/") + fnm;
var directory = new DirectoryInfo(HostingEnvironment.MapPath("~/Content/Images/"));
if (directory.Exists == false)
{
directory.Create();
}
ViewBag.FilePath = filePath.ToString();
file.SaveAs(filePath);
return RedirectToAction("Index", new { Message = ManageMessageId.PhotoUploadSuccess });
}
else
{
return RedirectToAction("Index", new { Message = ManageMessageId.FileExtensionError });
}
}
return RedirectToAction("Index", new { Message = ManageMessageId.Error });// PRG
}
private async Task<ApplicationUser> GetCurrentUserAsync()
{
return await UserManager.FindByIdAsync(User.Identity.GetUserId());
}
I opened a default MVC project that comes with visual studio and I added these extra things that I followed from this tutorial ASP.NET upload images. How do I resolve this?
Edit:
This is my RemovePhoto action.
public ActionResult RemovePhoto()
{
string file = "~/Content/Images/" + User.Identity.GetUserId() + ".png";
if(System.IO.File.Exists(Server.MapPath(file)))
System.IO.File.Delete(Server.MapPath(file));
return View("Index");
}
Just Redirect back to your Index action. That way you don't have to instantiate your Index model in your RemovePhoto action. Can read more about this pattern here.
In the create view what i am trying to do is when you choose a name from the dropdown list to fill the Login html.TextBoxFor automatically with his details.
Currently the Login textbox remains empty when i choose a person from dropdown list.
So i ve got my json object and tested as well my sql which is fine so i suppose the issue must be somewhere in jquery.
I would be glad if you could help me find the error.
View :
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<div class="editor-label">#Html.LabelFor(model => model.UserLogin)</div>
<div class="editor-field">#Html.TextBoxFor(model => model.UserLogin, new {id ="LoginId" })
#Html.ValidationMessageFor(model => model.UserLogin)</div>
<div class="editor-label">#Html.LabelFor(model => model.UserFullName)</div>
<div class="editor-field">#Html.DropDownList("UserFullName", ViewBag.UserFullName as SelectList, "Select a User", new { id = "UserID" })
#Html.ValidationMessageFor(model => model.UserFullName)</div>
<p>
<input type="submit"
value="Create" />
</p>
</fieldset> }
<div>#Html.ActionLink("Back to List", "Index")</div>
<script type="text/javascript">
$('#UserID').on('change', function () {
$.ajax({
type: 'POST',
url: '#Url.Action("GetUserForm")',
data: { FullName: $('#UserID').val() },
success: function (results){
var login = $('#LoginId');
login.empty();
$.each(results, function ()
{
login.val(this.ID).text(this.Value);
});
}});
});
</script>
Controller:
public ActionResult Create()
{
var names = StaffDB.StaffData.AsEnumerable().Select(s => new
{
ID = s.ID,
FullName = string.Format("{0} {1}", s.Forename1, s.Surname)
}).ToList();
if(ModelState.IsValid)
{
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserFullName = new SelectList(names, "FullName", "FullName", user.UserFullName);
return View(user);
}
[HttpPost]
public JsonResult GetUserForm(string FullName)
{
//pseudo code
var data = from s in StaffDB.StaffData
where s.Forename1 + ' ' + s.Surname == FullName
select new
{
Value = s.Login,
ID = s.ID
};
return Json(data);
}
I think the issue is while returning the json, In MVC by default Jsonresult is "Deny get", so you have add "Allow Get".
[HttpPost]
public JsonResult GetUserForm(string FullName)
{
//pseudo code
var data = from s in StaffDB.StaffData
where s.Forename1 + ' ' + s.Surname == FullName
select new { Value = s.Login, ID = s.ID };
if (data == null)
return Json(null);
return Json(data , JsonRequestBehavior.AllowGet);
}