Upload a File with MVC HttpPostedFileBase == null - asp.net-mvc

My Controller
public ActionResult Edit(int id)
{
return this.EditDefault(id);
}
[HttpPost]
public ActionResult Edit(int id, Models.Company model)
{
return this.EditDefault(id, model);
}
My Model
pulbic class Company
{
... Many other Propeties
public HttpPostedFileBase File { get; set; }
}
My View
#using (Html.BeginForm(new { enctype = "multipart/form-data" }))
{
... Many other Properties
#Html.TextBoxFor(m => m.File, new
{
type = "file", style = "display:none"
})
... Submit
}
So my problem now is when I submit the page the infos in the model are right, but the File Property is still null.
I found some solutions where people added HttpPostedFileBase as parmeter in the controller (tried it doesn't work too), but I would like to avoid that anyway because the model and the controller are generated with T4. So has someone an idea why the File Property is always null?
Would be really happy about some help :)
Update: Found the solution thx to Matt Tabor.
For me the solution looks like this because I use a shared Edit page.
The javascript part is to hide the actual file upload element and use a span instead, because the file upload isn't style able.
//Shared Part
#{
RouteData routeData = this.ViewContext.RouteData;
string currentController = routeData.GetRequiredString("controller");
}
#using (Html.BeginForm("Edit", currentController, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
//Special Part
... Many other Properties
//File upload which is hidden
#Html.TextBoxFor(m => m.File, new
{
type = "file", style = "display:none"
})
//Span which forwards the clicks to the file upload
<span id="fake-file-name">Kein Bild</span>
... Submit
}
<script type="text/javascript">
$(function () {
//forward the click from the span to the file upload
$("#fake-file-name").click(function () {
$("#File").click();
});
//display the chosen file name to the user with the styled span
$("#File").bind('change', function () {
//we don't want the C:\fakepath to show
var displayFileName = this.value.replace("C:\\fakepath\\", "");
$("#fake-file-name").text(displayFileName);
});
});
</script>

you need to specify your form method as post
#using (Html.BeginForm("Edit", "CONTROLLER", null,FormMethod.Post, new { enctype = "multipart/form-data" }))

#Html.TextBoxFor(m => m.File, new
{
type = "file", style = "display:none"
})
Instead have a Input type file as shown below -
<input type="file" name="File" id="File"/>
PS: Name should match to Model property name.
UPDATE
Remove display:none from your code and it should work fine.

Related

Ajax.beginForm not posting Files to Controller

I am working on MVC. I have a form to send Email. The razor form is below
#Ajax.BeginForm("sendEmail", "communication", new AjaxOptions() { OnBegin = "return onBeginFun();", UpdateTargetId = "emailMessage" }, new { id = "communicationForm", enctype = "multipart/form-data" })
{
#Html.TextAreaFor(m => m._Email.Body, new { #class = "form-control editor overflow:scroll; max-height:300px", name = "emailbody", id = "emailbody" })
<input type="file" id="fileUploader" name="fileUploader" multiple ;" />
<button type="submit" id="submitCommunication"> SAVE>>></button>
}
The controller is as follows
[HttpPost]
[ValidateInput(false)]
public async Task<JsonResult> sendEmail(CommunicationModelView P_CMV, HttpPostedFileBase fileUploader)
{
....................
}
Upon clicking submit button I can see the Model filled with the form data, but fileUploader is null even though I have selected file(s).
Need help.
Thanks in advance.

How to go to postback on change of #Html.EditorFor in mvc?

I have a #Html.EditorFor that represents a search field. I am trying to do a search in the controller when the text in the field is changed.
I can't figure out how to go to the postback every time the text is changed in the input, and not when the submit button is clicked.
Model:
public class MainWindow
{
[Key]
public int MainWindowId { get; set; }
public string SearchedString { get; set; }
}
View:
#using (Html.BeginForm())
{
<div>
<label>search:</label>
#Html.EditorFor(model => model.SearchedString, new htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SearchedString, "", new { #class = "text-danger" })
<input type="submit" value="search" class="btn btn-default" />
</div>
}
Controller:
[HttpPost]
public ActionResult Index([Bind(Include = "MainWindowId,SearchedString")] MainWindow mw)
{
ManageProduct mp = new ManageProduct();
if (ModelState.IsValid)
{
//search code
return View("Index",mw);
}
return View(mw);
}
Use AJAX to submit the form. I will use jQuery in my example.
Listen to changes of the <input> rendered by the Editor.
EDIT: To achieve this, we use the ID that the editor gave to the HTML input as jQuery selector (do not change the ID, because the MVC modelbinder expects it to be in a certain format). Find the ID using the browser Developer Tools (F12).
You will also need to give an ID to the form element so we can serialize it to get the post data. Also provide a placeholder into which to render the results.
#using (Html.BeginForm("Index", "YourController", FormMethod.Post, new { id = "formId" })) {
<div id="placeholderId"></div>
#Html.EditorFor(model => model.SearchedString, new htmlAttributes = new { #class = "form-control" } })
JS function to post the form, embedded in the razor view:
<script type="text/javascript">
function postForm() {
$.ajax({
url: $('#formId').attr('action'),
type: 'POST',
data: $('#formId').serialize(),
success: function(resultData) {
if (resultData) {
// update some placeholder element with the received data
$('#placeholderId').html(resultData);
}
}
});
}
JS to listen to changes of the input, embedded in the razor view. Listen to the keyup event, because change will only fire after the input loses focus. Here I assume that the editor gave id="SearchedString" to the input (may vary, e.g. if this is rendered as partial view).
$('#SearchedString').on('keyup', function () {
postForm();
});
</script>
To prevent your server beeing swamped with request while the user types, take a look at jQuery.debounce

#Html.DisplayFor() value not change after the postback

#Html.DisplayFor() value not change after send data. I read a article about this issue and say it like this; only send data what such as EditorFor, TextBoxFor, TextAreaFor and change state. Is it true? How can I change this value after the postback?
View
#model HRProj.Model.Person
#using(Html.BeginForm("Skills", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })){
#Html.HiddenFor(m => m.SkillDoc.Filename)
<span class="file-upload">
<span>Choose a file</span>
<input type="file" name="file" />
</span>
File name : #Html.DisplayFor(m => m.SkillDoc.Filename)
<button>Upload</button>
}
Controller
public ActionResult Skills(int? id)
{
Others oparations...
var model = new Person { SkillDoc = db.GetSkillDoc().FirstOrDefault(m => m.PersonId == id) };
return View(model);
}
[HttpPost]
public ActionResult Skills(Person model, HttpPostedFileBase file)
{
Others oparations...
if (ModelState.IsValid)
{
SkillDoc doc = new SkillDoc();
doc.Id = model.SkillDoc.Id;
doc.PersonId = model.SkillDoc.PersonId;
doc.CvDoc = (file != null) ? file.FileName : model.SkillDoc.CvDoc;
db.SkillDocCRUD(doc, "I");
TempData["eState"] = "The record adding successfully";
if (file != null)
{
file.SaveAs(Server.MapPath("~/Files/" + file.FileName));
}
}
return View(model);
}
Please add the following line inside the if block:
model.SkillDoc=doc;
Or rather redirect to Skills action:
return RedirectToAction("Skills", new{id= model.PersonId});

How can send List<> Model From View To Controller using Ajax Jquery in Mvc3 asp.net?

How Can I Send List<int> Roles Model? For example from view To Controller.
Using Ajax Jquery in Mvc3 asp.net not razor.
I'm using this code
var url = '<%:Url.Action("Roles","RolesManager") %>';
$.ajax({
url: url,
type: 'post',
dataType: "json",
traditional: true,
data: $('#EditUserForm').serialize(),
success: function () {
$('#EditUserForm').submit();
},
error: function () {
}
});
but when I debug the controller List<int> Roles = null.
mode in page like
<%: Html.ListBoxFor(m => m.UserRoles, new MultiSelectList(Model.UserRoles, "UserRoleId", "UserRoleName"), new { #id = "UserRoles", #class = "ddlUserRolesCls" })%>
It's a Model Bind List, in this case, you have to send the information using the same name of your parameter in the name attribute of html inputs tag and asp.net model binder will convert it on a collection on the post. Your javascript looks fine. Try something like this:
In the view:
<input type="text" name="roles" value="1" />
<input type="text" name="roles" value="4" />
<input type="text" name="roles" value="2" />
<input type="text" name="roles" value="8" />
in the controller:
public ActionResult Post(List<int> roles)
{
// process
}
Also take a look at this article:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Change your viewmodel like this. Note that we have a SelectedUserRoles property of type int array.
public class EditUserRole
{
public List<SelectListItem> UserRoles{ get; set; }
public int[] SelectedUserRoles { set; get; }
//Also other relevant properties here
}
And in my Get Action, fill the UserRoles property
public ActionResult EditUser(int id)
{
var vm=new EditUserRole();
vm.UserRoles=GetUserRoles();
}
public List<SelectListItem> GetUserRoles()
{
var roles= new List<SelectListItem>();
// the below is hardcoded. Get it from DB And fill it here
roles.Add(new SelectListItem { Value="1",Text="Admin" });
roles.Add(new SelectListItem { Value = "2", Text = "Editor" });
return roles;
}
and in your view which is strongly typed to EditUserRole,
#Html.ListBoxFor(m => m.SelectedUserRoles,
new MultiSelectList(Model.UserRoles, "Value", "Text"),
new { #id = "UserRoles", #class = "ddlUserRolesCls" })
When you post your form, you will get the selected Roles ID in the SelectedUserRoles property of the posted model.
[HttpPost]
public ActionResult Edit(EditUserRole model)
{
// check model.SelectedUserRoles
// to do : Save and redirect
}

MVC - file upload

Hey...
I have upload control on my view. Is there a way to associate this control with model data(something like LabelFor or TextBoxFor). I need this, because on page load I loose my information in file upload control
Thx
HTML Upload File ASP MVC 3.
Model: (Note that FileExtensionsAttribute is available in MvcFutures. It will validate file extensions client side and server side.)
public class ViewModel
{
[Required, Microsoft.Web.Mvc.FileExtensions(Extensions = "csv", ErrorMessage = "Specify a CSV file. (Comma-separated values)")]
public HttpPostedFileBase File { get; set; }
}
HTML View:
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(m => m.File, new { type = "file" })
#Html.ValidationMessageFor(m => m.File)
}
Controller action:
[HttpPost]
public ActionResult Action(ViewModel model)
{
if (ModelState.IsValid)
{
// Use your file here
using (MemoryStream memoryStream = new MemoryStream())
{
model.File.InputStream.CopyTo(memoryStream);
}
}
}
Yes, use the HttpPostedFileBase class for the property type and it will bind just like any other property would.

Resources