Can I accept a HttpPostedFileBase as part of a ViewModel - c#

I am setting up a view and ViewModel to accept some data and a File. But when I look at the returned model, I do not see a file.
Here's my ViewModel:
public class ResourceReviewViewModel
{
public Guid ResourceReviewId { get; set; }
//....
[Display(Name="Review Document File")]
public HttpPostedFileBase ReviewFile { get; set; }
}
My view:
My controller to handle the submit:
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult ResourceReview(ResourceReviewViewModel model)
{
//...
return View(model); // model.ReviewFile is null
}
#model PublicationSystem.ViewModels.ResourceReviewViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Submit Your Review</h4>
<hr/>
#Html.HiddenFor(model => model.RequestReviewId, new { htmlAttributes = new { #class = "form-control" } })
<div class="form-group">
#Html.LabelFor(model => model.ReviewFile, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="ReviewFile" name="ReviewFile" value="ActionHandlerForForm" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
</div>
}
The model comes back with values, but the file property is NULL. The HTML for the Input I copied from another page, so I'm not sure I need value="ActionHandlerForForm".

Yes, it's possible. I think in your example you're just missing multipart/form-data
Try this (replace "Index" and "Home" to the View/Controller you have):
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}

Related

C# asp.net issue on Form with file type and text

I'm going straight to the point here guys,
I have a form. when I save the form... it only gets the firstname, middlename and lastname.. it doesn't get the files... however, if I only get the photo and comment out other inputs... the photo is captured on my model.. I dunno why it behaves like this.. I'm really new to asp.net mvc.. so please bear with me..
#model Impulse.ViewModels.AgentViewModel
#{
ViewBag.Title = "AgentForm";
Layout = "~/Views/Shared/_SiteLayout.cshtml";
}
<div class="custom-container">
<h1 class="title"><strong>Add New Agent</strong></h1>
<hr />
#using (Html.BeginForm("Save", "Agent", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="row">
<div class="col-md-3">
<div id="preview">
<img src="~/Content/Assets/no-image.png" id="profile_image" class="img-thumbnail" />
</div>
<div class="form-group">
<label>Profile Picture</label>
<input type="file" name="photo" id="photo" />
</div>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-4">
<div class="form-group">
#Html.LabelFor(m => m.Agent.FirstName)
#Html.TextBoxFor(m => m.Agent.FirstName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Agent.FirstName)
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
#Html.LabelFor(m => m.Agent.MiddleName)
#Html.TextBoxFor(m => m.Agent.MiddleName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Agent.MiddleName)
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
#Html.LabelFor(m => m.Agent.LastName)
#Html.TextBoxFor(m => m.Agent.LastName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Agent.LastName)
</div>
</div>
</div>
</div>
</div>
<input type="submit" class="btn btn-primary" value="Save" />
}
</div>
Controller
[HttpPost]
public ActionResult Save(AgentModel agent)
{
//I debug here to see the data passed by my view
return Content("Sample");
}
Model
public class AgentModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
[FileSize(10240)]
[FileTypes("jpg,jpeg,png")]
public HttpPostedFileBase photo { get; set; }
}
you can try like this
Model
public class UploadFileModel
{
public UploadFileModel()
{
Files = new List<HttpPostedFileBase>();
}
public List<HttpPostedFileBase> Files { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
}
View
#using (Html.BeginForm("UploadData", "Home", FormMethod.Post, new { encType="multipart/form-data" }))
{
#Html.TextBoxFor(m => m.FirstName)
<br /><br />
#Html.TextBoxFor(m => m.Files, new { type = "file", name = "Files" })<br /><br />
<input type="submit" value="submit" name="submit" id="submit" />
}
Controller
public ActionResult UploadData(UploadFileModel model)
{
var file = model.Files[0];
return View(model);
}
you are binding to view to AgentViewModel so you will get AgentViewModel when you post server. so the parameter to action save should be viewmodel. Or Else change view to bind to AgentModel.
The file control that you have used is html input type. try using below code.
#Html.TextBoxFor(m => Model.File, new { type = "file" , accept=".pdf"})
#Html.ValidationMessageFor(m => Model.File)

Varying amount of fields in Razor page form, returns null properties in view model

I've come up with problem, when trying to fill data to my model. I have an "Resource" entity, which can have no-to-many "attributes". I have templates set up, which holds names for those attributes. When Resource is created, user chooses on of templates, then Attributes are created(empty) and program generates form for those attributes.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(Model => Model.Resource)
#for (int i = 0; i < Model.Attributes.Count(); i++)
{
<div class="form-group">
#*Html.LabelFor(d => d.Attributes.ToArray()[i].Name, htmlAttributes: new { #class = "control-label col-md-2" })*#
<h4>#Html.Raw(Model.Attributes.ToList()[i].Name)</h4>
<div class="col-md-10">
#Html.TextBoxFor(Model => Model.Attributes.ToList()[i].Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(Model => Model.Attributes.ToList()[i].Value, "", new { #class = "text-danger" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
This form uses this View model:
public class ResourceAttributesViewModel
{
public virtual Resource Resource { get; set; }
public virtual ICollection<_Attribute> Attributes { get; set; }
}
problem is that when i hit "submit" button, it gives me view model with null Resource and Attributes properties
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Fill(ResourceAttributesViewModel AttributeSet)
{
if(ModelState.IsValid)
{
foreach (var attr in AttributeSet.Attributes)
{
db.Entry(attr).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("Index");
}
return View(AttributeSet);
}
if it helps, there is POST string sent by browser
__RequestVerificationToken=XoVM9h_3njX5x2m35b_vKKHY3m5UDaYm9_2ZMfNkglouqHJCSw2NO56Tv2Sb3kXy8qC8XBLXawoQv0ft0xc-LxYmQGfi4EAqroq2b63Wb9Q1&Resource=System.Data.Entity.DynamicProxies.Resource_7639327FA0332BEBC7FB6836F70C3D62C3D744D76F2C3F8DDFCE679AA8CA31DC&%5B0%5D.Value=100&%5B1%5D.Value=200

View model parameter is null in HttpPost action method

I am redirecting my viewModel from the HttpPost of View1 to the HttpGet of View2.
This works without problems.
There the user has to accept the terms and agreemens.
This should get changed in the viewModel to true (before it is false).
And then redirect to the HttpPost of view2.
There something goes wrong.
The HttpPost ActionResult of View2 receives the viewModel with all Parameters as NULL (before they were filled)
How can I fix this?
Here is my HttpGet ActionResult for View2:
public ActionResult Verify(QuestionViewModel viewModel)
{
//Anrede in Viewbag
if (viewModelVeri.Per_Salutation == 2)
{
ViewBag.Per_Salutation = "Frau";
}
else
{
ViewBag.Per_Salutation = "Herr";
}
int? per_region_id = viewModelVeri.Per_Region;
int per_region_id_nullable = Convert.ToInt32(per_region_id);
Region region = _repository.GetRegionById(per_region_id_nullable);
QuestionViewModel viewModel2 = new QuestionViewModel()
{
Reg_Name = region.Reg_Name
};
//Regionsname in Viewbag
ViewBag.Reg_Name = viewModel2.Reg_Name;
return View(viewModel);
}
And here's my HttpPost ActionResult for View2:
[HttpPost]
public ActionResult Verify(QuestionViewModel viewModel, string tbButton)
{
//here the ViewModel-Parameters are already NULL
My View:
<div class="panel-body">
#using (Html.BeginForm("Verify", "QuestionForm", FormMethod.Post, new { id = "verifyform" }))
{
#Html.AntiForgeryToken()
<div class="ctrl-row">
<div class="form-group">
<div class="container-fluid">
#Html.LabelFor(model => model.Per_Salutation, new { #class = "control-label col-sm-1" })
<div class="col-sm-3">
#ViewBag.Per_Salutation
</div>
</div>
</div>
</div>
<div class="ctrl-row">
<div class="form-group">
<div class="container-fluid">
#Html.LabelFor(model => model.Per_Name_Last, new { #class = "control-label col-sm-1" })
<div class="col-sm-3">
#Html.DisplayFor(model => model.Per_Name_Last, new { #class = "control-label col-sm-1 non-zero-num" })
</div>
#Html.LabelFor(model => model.Per_Name_First, new { #class = "control-label col-sm-1" })
<div class="col-sm-3">
#Html.DisplayFor(model => model.Per_Name_First, new { #class = "control-label col-sm-1 non-zero-num" })
</div>
</div>
</div>
</div
<div class="ctrl-row">
<div class="form-group">
<div class="container-fluid">
#Html.LabelFor(model => model.Per_EMail, new { #class = "control-label col-sm-1" })
<div class="col-sm-8">
#Html.DisplayFor(model => model.Per_EMail, new { #class = "control-label col-sm-1 non-zero-num" })
</div>
</div>
</div>
</div>
<div class="checkbox">
<input type="checkbox" id="NutzungsbedingungenAngenommen " />
<label for="NutzungsbedingungenAngenommen ">
Ich erkläre mich mit den Nutzungsbedingungen einverstanden.
</label>
</div>
<button class="btn btn-default" type="submit" name="tbButton" value="questsend">Senden</button>
}
<script>
$(document).ready(function () {
$(".non-zero-num").val($(this).val() == 0 ? ' ' : $(this).val());
})
$('#verifyform').on('click', '[value="questsend"]', function () {
if ($('#agree').is(':checked')) {
return true;
}
else {
return false;
}
});
</script>
EDIT
Here my QuestionViewModel
public class QuestionViewModel
{
//Other Properties
[Required(ErrorMessage = "Bitte die Nutzungsbedingungen annehmen!")]
public bool NutzungsbedingungenAngenommen { get; set; }
}
My HttpPost Controller for View1:
[HttpPost]
public ActionResult DefaultForm(QuestionViewModel viewModel, string tbButton)
{
if (ModelState.IsValid)
{
try
{
if (tbButton.Equals("questsend"))
{
return RedirectToAction("Verify", viewModel);
}
else if (tbButton.Equals("questupload"))
{
//write to DB
return View(viewModel);
}
else
{
dropdownPopulate(viewModel);
return View("DefaultForm", viewModel);
}
}
catch
{
dropdownPopulate(viewModel);
return View(viewModel);
}
}
else
{
dropdownPopulate(viewModel);
return View(viewModel);
}
}
The problem is you use Html.DisplayFor to display the property values of viewModel in View2, so the values won't be submitted to the HttpPost method, hence viewModel is null when HttpPost for View2 is executed. Only values in <input>, <textarea> and <select> tags will be submitted.
You can submit the values of viewModel to the HttpPost for View2 by adding Html.HiddenFor inside Html.BeginForm for all properties of viewModel. You should also use Html.CheckBoxFor(m => m.NutzungsbedingungenAngenommen) for the checkbox. Something like below should work
#using (Html.BeginForm("Verify", "QuestionForm", FormMethod.Post, new { id = "verifyform" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Per_Salutation)
#Html.HiddenFor(m => m.Per_Name_First)
#Html.HiddenFor(m => m.Per_Name_Last)
.... // Html.HiddenFor for the rest of QuestionViewModel properties
....
.... // the rest of your code inside the form tag
.... // remove <input type="checkbox" id="NutzungsbedingungenAngenommen " />
#Html.CheckBoxFor(m => m.NutzungsbedingungenAngenommen)
<button class="btn btn-default" type="submit" name="tbButton" value="questsend">Senden</button>
}
Make sure you declare what model should be used by Razor.
#model QuestionViewModel
Make sure the name and id of your HTML inputs are in the format expected by the MVC modelbinder. I recommend using the provided HtmlHelpers instead of writing the input tags by hand.
#Html.CheckBoxFor(m => m.Agree)
Remove the string tbButton parameter from your POST action

ASP .Net MVC Form not calling controller on submit

I am having trouble getting my view to call the post method in my MVC Controller. When I click on the submit button, it does not call the Create method. I add a breakpoint, but it never gets to the code. I am assuming some error, but not sure how to see the error message.
Here is the View:
#model PersonViewModel
#{
ViewBag.Title = "Register";
}
#using (Html.BeginForm(PeopleControllerAction.Create, ControllerName.People, FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal row">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
</div>
<div class="row panel radius">
<div class="medium-2 columns">
<h3>Contact Information</h3>
</div>
<div class="medium-10 columns">
<div class="row">
<div class="medium-6 columns">
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
</div>
// more fields removed for brevity
<div class="row">
<div class="form-group">
<div>
<input type="submit" value="Submit" class="button" />
</div>
</div>
</div>
}
Here is the controller:
public class PeopleController : Controller
{
private IPersonService context { get; set; }
public PeopleController(IPersonService context)
{
this.context = context;
}
// POST: People/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "FirstName,LastName,Age,Email,Phone,City,State,HopeToReach,Story,Goal,Image")] PersonViewModel person)
{
if (ModelState.IsValid)
{
try
{
person.ImagePath = ImageUploader.UploadImage(person.Image);
}
catch (ArgumentException e)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Message);
}
var id = await context.AddAsync(person);
return RedirectToAction(PeopleControllerAction.Confirmation);
}
return View(person);
}
}
This was resolved. The action in question did not have a route. I am using attribute routing.

Display template for editing a list attribute in model returns null on form submit

I have a model that contains a class like Days where Days are a collection of Day.
This is what the entity framework autogenerated model looks like:
public MyModel()
{
this.ExceptionDays = new HashSet<ExceptionDay>();
this.RegularDays = new HashSet<RegularDay>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ExceptionDay> ExceptionDays { get; set; }
public virtual ICollection<RegularDay> RegularDays { get; set; }
}
The RegularDay & ExceptionDay are, both, separate classes in separate files under the autogenerated model.
Now, the create form for this model needs to take a Day and add it to the list Days. I figured I'd use a display template for doing this.
This is what my create display view looks like:
#model MyModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
<div class="form-group">
<div class="col-md-10">
**#Html.EditorFor(model => model.RegularDays, "ICollection_RegularDay_Edit")**
</div>
</div>
<div class="form-group">
<div class="col-md-10">
**#Html.EditorFor(model => model.ExceptionDays, "ICollection_ExceptionDay_Edit")**
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
My Edit template looks like this:
#model RegularDay
<div class="form-group">
#Html.LabelFor(model => model.dayOfWeek)
<div class="col-md-">
<div class="col-md-">
#Html.DropDownListFor(model => model.dayOfWeek, new SelectList(
new List<Object>
{
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
}
))
</div>
#Html.ValidationMessageFor(model => model.dayOfWeek, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.startTime)
<div class="col-md-">
#Html.EditorFor(model => model.startTime, new { htmlAttributes = new { #class = "form-control" } })
#*#Html.ValidationMessageFor(model => model.startTime, "", new { #class = "text-danger" })*#
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NumberOfHours)
<div class="col-md-">
#Html.DropDownListFor(model => model.NumberOfHours, new SelectList(
new List<Object>
{
1,2,3,4,5,6,7,8
}
))
</div>
</div>
The other display template for edit is similar.
Now the problem is, that my controller never gets the regularDay or ExceptionDay in the model that the view returns on post.
My controller method looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,Description, RegularDays, ExceptionDays")] MyModel myModel)
{
if (ModelState.IsValid)
{
db.LocationHoursModels.Add(locationHoursModel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(myModel);
}
How can I go about creating a display template or a create view for the Days type of attribute for this MVC?

Categories