I have the following code, but it return me an empty FormationDTO object, did I do anything wrong?
I don't understand why it can't properly bind FormationFormViewModel's FormationDTO to the action parameter FormationDTO, it worked in others controllers.
FormationsController
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(FormationDTO formation)
{
if (!ModelState.IsValid){
return View("FormationForm", new FormationFormViewModel { FormationDTO = formation, Categories = GetCategories() });
}
else{
// DO THE STUFF
}
}
FormationForm.cshtml
#model BSS_IT_Education.Models.FormationFormViewModel
#{
ViewBag.Title = "Formation";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Save", "Formations"))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.FormationDTO.Id)
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.FormationDTO.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(model => model.FormationDTO.Name, new { htmlAttributes = new { #class = "form-control", #placeholder = "Entrez le nom de la formation..." } })
#Html.ValidationMessageFor(model => model.FormationDTO.Name, "", new { #class = "text-danger" })
</div>
</div>
// BUNCH OF OTHERS FORM-GROUPS
<div class="form-group">
<div class="col-md-offset-2 col-md-8">
<button type="submit" class="btn btn-success">#((Model.FormationDTO.Id == 0) ? "Sauvegarder " : "Modifier")</button>
</div>
</div>
</div>
}
If I am understanding the code correctly. It looks like you should be passing FormationFormViewModel to the controller action. Not FormationDTO.
Take a look at the generated HTML on the page. I'm guessing the name attributes on your input elements will look something like formationDTO.name, because your ViewModel is a FormationFormViewModel. But the ModelBinder on the backend is going to look for just a property name, because you are trying to build a FormationDTO.
You may need to manually create those input elements, or use a child action to get the correct ViewModel to a view that lets you use the razor #Html helpers to build the correct elements.
Or, the easier option is to make your controller action accept a FormationFormViewModel, then the ModelBinder should correctly build out the properties of the FormationDTO you want.
Related
I am creating an MVC application in .NET Framework 4.7.2. I have a few controllers in this application and they have all worked fine before. However, with the latest controller, I am getting a mystery bug. The controller is (I have removed methods irrelevant to this question):
namespace MyApp.Controllers
{
[Authorize(Roles = "Admin")]
public class DatabaseController : Controller
{
[HttpGet]
public ActionResult EditTableDescription(string name)
{
Table table = new Table(name);
table.GetDescription();
return View(table);
}
[HttpPost]
public ActionResult EditTableDescription(Table table)
{
if (ModelState.IsValid)
{
// Updates description only
table.UpdateDescription();
return RedirectToAction("ViewTables", "Database");
}
return View();
}
}
}
whilst the view the GET call serves is defined by
#model MyApp.Models.Database.Table
#{
ViewBag.Title = "EditTableDescription";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Name)
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", 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>
}
The [HttpGet] decorated method works fine, serving up the page. However, on submitting the form, I get an error saying Failed to load resource: the server responded with a status of 500.
Note that:
I have other Controllers/Actions that (so far as I can see) follow exactly the same pattern and work fine.
I have tried adding the name of the method and controller to the Html.BeginForm() method to no avail.
The [Authorize(Roles = "Admin")] decoration makes no difference.
For some reason, the [HttpPost] decorated method just does not seem to be seen (and is not called). Anyone have any ideas why this might happen?
This question already has answers here:
Can the ViewBag name be the same as the Model property name in a DropDownList?
(2 answers)
Closed 5 years ago.
I'm just trying to put a SelectList into the ViewBag but I can't access it right after.
Here's my code:
// GET: Content/CreateSerie
public ActionResult CreateSerie()
{
ViewBag.originalLang = new SelectList(db.Lang, "originalLang", "originalLang");
return View();
}
If I use the debugger to step right after the ViewBag.originalLang assignation and use the expression evaluator, I get
However, if I go deeper into the ViewBag I can see
This is really weird and I don't get why I can't access it normally. Of course, I can't access it from the view either.
EDIT: Here's my View as erdi yılmaz requested:
#model e08projh17.Models.Content
#{
ViewBag.Title = "Créer une série";
#Styles.Render("~/Content/css")
#Styles.Render("~/Content/themes/base/css")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
}
<h2>CreateSerie</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Content</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<!-- Lots of stuff ... -->
<div class="form-group">
#Html.LabelFor(model => model.originalLang, "Langue originale", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.originalLang, (SelectList)ViewBag.originalLang, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.originalLang, "", new { #class = "text-danger" })
</div>
</div>
<!-- Lots of stuff ... -->
<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>
}
<div>#Html.ActionLink("Back to List", "Index")</div>
#section Scripts { #Scripts.Render("~/bundles/jqueryval") }
Try this:
#Html.DropDownList("originalLang", null, htmlAttributes: new { #class = "form-control" } })
In your controller, you aren't returning a model to the view.. return View(/* empty here */);
So I don't know why you are using DropDownListFor. Instead use DropDownList
This is because the ViewBag is a dynamic type which means that it is resolved at runtime.
If you step through the debugger, you are looking at precompiled code and the ViewBag object has not yet been resolved by the DLR so you are unable to resolve the property.
What you are seeing inside the ViewBag when you "go deeper" are implementation details of how the data is collected for resolution.
Try this for dropdown
#Html.DropDownListFor(model => model.originalLang, (IEnumerable<SelectListItem>)ViewBag.originalLang, new { htmlAttributes = new { #class = "form-control" } })
I'm going to create profile for my users in ASP.Net MVC application. Users creation controller is something like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(UserProfileViewModel userViewModel)
{
if (ModelState.IsValid)
{
....
}
return View(userViewModel);
}
Besides, each user model got several properties including one photo. Everything goes well till I want to add an input field to accept photo.
#Html.TextBoxFor(model => model.ImageUrl, new { type = "file" })
And I add below field to UserProfileViewModel:
[Display(Name = "Profile Photo")]
public HttpPostedFileBase ImageUrl { get; set; }
Among snippets to upload a photo and answers to my previous question, it seems uploading photo was considered as a separate task. i.e. I need an individual form and controller to upload a photo (like first part of this answer).
I want to know are there any methods that I can create whole user profile in one form and pass its photo to the same controller (which included photo in UserProfileViewModel)?
I need to note I don't know to use jQuery or AJAX and I want to use standard HTML helpers for this task.
Update: My View Looks like this:
#model ProjectManager.Models.UserProfileViewModel
#{
ViewBag.Title = "Create";
}
<h2>#ViewBag.Title</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>User Profile</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Description, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Age, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageUrl, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.ImageUrl, new { type = "file" })
#Html.ValidationMessageFor(model => model.ImageUrl)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="ثبت" class="btn btn-default" />
</div>
</div>
</div>
}
<div class="rtl text-right">
#Html.ActionLink("Back To List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
File inputs are not sent in the request unless your form element contains the enctype = "multipart/form-data" attribute. Change the view code to
#using (Html.BeginForm("Create", "User", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
....
}
All i have got, your question is I want to know are there any methods that I can create whole user profile in one form and pass its photo to the same controller (which included photo in UserProfileViewModel)?
Yes. It is possible. If you overwrite the form as Stephen Muecke said, you should get the photo with viewmodel. If you get null in viewmodel, you can retrieve the file(photo) from the request also.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(UserProfileViewModel userViewModel)
{
if (ModelState.IsValid)
{
HttpPostedFileBase fileUploadObj= Request.Files[0];
//for collection
HttpFileCollectionBase fileUploadObj= Request.Files;
....
}
return View(userViewModel);
}
Hope this helps :)
You need to use an BeginForm() that allows you to add htmlAttributes, and because you need to add
new {enctype = "multipart/form-data" }
#using (Html.BeginForm("UserProfileViewModel ", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UserProfileViewModel(UserProfileViewModel userViewModel)
{
if (ModelState.IsValid)
{
HttpPostedFileBase fileUpload= Request.Files[0];
//for collection
HttpFileCollectionBase fileUpload= Request.Files;
....
}
Hit a strange issue where my model is not binding and shows up on the controller as null.
I have a form doing a httppost. My breakpoint in the controller is hit and the parameter I expect to be my model is null.
Looking at some example code on another page that works, I copied and pasted it and the only difference was the name of the parameter was 'model' instead of message.
View
#model Site.Models.ContactMessage
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ContactMessage</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Message, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.To, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.To, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.To, "", 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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
public ActionResult Contact()
{
return View();
}
[HttpPost]
public ActionResult Contact(ContactMessage message)
{
var m = message;
return View();
}
and it worked. I thought I must have entirely missed something about naming convention. Found you can use Bind, from reading a heap of other posts, to change the prefix like;
public ActionResult Contact([Bind(Prefix = "model")] ContactMessage message)
but that didn't work, still null. Going to rename it to model so it works and I can move on but would like to know why it's not binding if not called model.
public ActionResult Contact(ContactMessage message)
Changed back to this as above but still returns a null.
Interestingly, if I open up another MVC app, that one has whatever parameter names I want and works fine. It's using an older version of MVC 5 (not updated it yet but I will do that and see if anything happens. I don't expect it will.)
Your problem is that you model contains a property named Message and you also have a parameter named message The DefaultModelBinder reads the form values which will include message = "someTextValue" and searches for model properties that have the name message. It finds the one in you model and sets it value (all OK so far) but then it finds another one (your parameter) and tries to set the value of a complex object string value (in effect ContactMessage message = "someTextValue";) which fails so the model becomes null
Hit a strange issue where my model is not binding and shows up on the controller as null.
I have a form doing a httppost. My breakpoint in the controller is hit and the parameter I expect to be my model is null.
Looking at some example code on another page that works, I copied and pasted it and the only difference was the name of the parameter was 'model' instead of message.
View
#model Site.Models.ContactMessage
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ContactMessage</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Message, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.To, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.To, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.To, "", 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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
public ActionResult Contact()
{
return View();
}
[HttpPost]
public ActionResult Contact(ContactMessage message)
{
var m = message;
return View();
}
and it worked. I thought I must have entirely missed something about naming convention. Found you can use Bind, from reading a heap of other posts, to change the prefix like;
public ActionResult Contact([Bind(Prefix = "model")] ContactMessage message)
but that didn't work, still null. Going to rename it to model so it works and I can move on but would like to know why it's not binding if not called model.
public ActionResult Contact(ContactMessage message)
Changed back to this as above but still returns a null.
Interestingly, if I open up another MVC app, that one has whatever parameter names I want and works fine. It's using an older version of MVC 5 (not updated it yet but I will do that and see if anything happens. I don't expect it will.)
Your problem is that you model contains a property named Message and you also have a parameter named message The DefaultModelBinder reads the form values which will include message = "someTextValue" and searches for model properties that have the name message. It finds the one in you model and sets it value (all OK so far) but then it finds another one (your parameter) and tries to set the value of a complex object string value (in effect ContactMessage message = "someTextValue";) which fails so the model becomes null