I have absolutely no idea why my model is null when I'm trying to submit this form with only ONE field, a dropdownlistfor selected value.
The Get works just fine, and the Model is defintively not null. But everytime I try to submit the form, model is always null and I have no idea why at the moment:
Model:
[Required]
public string SelectedOrderStatus { get; set; }
public List<SelectListItem> OrderStatuses { get; set; }
View:
#model Webstore.Models.OrderViewModel
#using (Html.BeginForm("Edit", "Order", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.OrderId)
<div class="form-horizontal">
<h4>Change Order Status for order: #Model.OrderId</h4>
<div class="form-group">
#Html.LabelFor(model => model.Orderstatus, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedOrderStatus, new SelectList(Model.OrderStatuses, "Value", "Text"))
#Html.ValidationMessageFor(model => model.SelectedOrderStatus)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save Order Status" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
[HttpPost]
public ActionResult Edit(Guid id, OrderViewModel model)
{
try
{
Order orderToEdit = _context.Orders.Find(id);
orderToEdit.Orderstatus = (Orderstatus)Enum.Parse(typeof(Orderstatus), model.SelectedOrderStatus);
_context.Entry(orderToEdit).State = EntityState.Modified;
_context.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
I would appreaciate a lot if you would help me out here!
Regards!
Try to check if FormCollection collection have value which you need. So your Edit methods will looks like:
public ActionResult Edit(Guid id, FormCollection collection)
{
// rest of logic here
}
Optionally, check in Request[..], like here:
public ActionResult Edit(Guid id)
{
var value1 = Request["SelectedOrderStatus"];
}
Of course this is not as beatifull solution as it should be, but there is some problem with model blinding which I cannot resolve without rest of code.
Related
Here is my view:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate.AspNet.Identity;
namespace MyProject.Web.Models
{
public class IdentityRoleView
{
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<IdentityUser> Users { get; set; }
}
}
Here is my controller:
[HttpGet]
public ActionResult Edit(string roleId)
{
IdentityRole role = Service.Find(roleId);
return View("Edit", AutoMapper.Mapper.Map<IdentityRoleView>(role));
}
[HttpPost]
public ActionResult Edit(IdentityRoleView role)
{
Service.Update(role);
TempData["Comment"] = "The record was updated";
return RedirectToAction("Index");
}
}
Here is my view:
#model MyProject.Web.Models.IdentityRoleView
#{
ViewBag.Title = "Edit";
Layout = "~/Areas/Administration/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id);
<div>
Role name
</div>
<p>
#Html.TextBoxFor(model => model.Name)
</p>
for (int items = 0; items < Model.Users.Count; items++)
{
#Html.DisplayFor(m => m.Users[items].Id);
#Html.DisplayFor(m => m.Users[items].UserName);
}
<input type="submit" value="Save" />
}
IdentityRoleView.Users is always null when it is passed back to the controller. What is the problem? I can see IdentityRoleView.Users on the view - they are all deleted when posted back to the server as IdentityRoleView.Users is always null.
This is a bit of a quirk of MVC.
What you have is mostly correct.
Your controller is just fine:
[HttpGet]
public ActionResult Index()
{
Person p1 = new Person();
p1.name = "Ian";
List<Sport> sports = new List<Sport>();
Sport s1 = new Sport();
s1.description = "Football";
sports.Add(s1);
Sport s2 = new Sport();
//s1.description = "Netball"; I'm guessing this is a typo?
s2.description = "Netball";
sports.Add(s2);
p1.sports = sports;
return View("Person", p1);
}
[HttpPost]
public ActionResult Index(Person p1)
{
return View();
}
However, its the view that has the issue:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Person</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.name, "", new { #class = "text-danger" })
</div>
</div>
#* This is the key difference here *#
#for (int items = 0; items < Model.sports.Count; items++)
{
<div>
#Html.DisplayFor(sport => Model.sports[items].description)
#Html.HiddenFor(sport => Model.sports[items].description)
<hr />
</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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
So let me explain.
What I did was moved your sports list into the Form(){}. This needs to be done in order to actually have the form pass the objects back. Whatever is in that Form is what it is going to post back.
Another thing you have to do is give the form something to actually bind the model object to.
The #Html.DisplayFor helper won't do it, and since it appears you don't want users changing the sports, you can simply create a #Html.HiddenFor. This will pass back the values to your [HttpPost] Index(Person p1) method.
What you do after that, is up to you.
Hope this helps.
Here is the viewmodel
public class IdentityRoleView
{
public IdentityRoleView()
{
Users = new List<IdentityUser>();
}
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<IdentityUser> Users { get; set; }
}
The Controller Methods
public ActionResult Edit(string roleId)
{
IdentityRoleView model = new IdentityRoleView();
model.Name = "IdentityRoleViewUser";
model.Id = "2";
model.Users.Add(new IdentityUser {
UserName = "testuser",
Id = "1",
Email = "test#test.com"
});
model.Users.Add(new IdentityUser
{
UserName = "testuser2",
Id = "2",
Email = "test#test2.com"
});
return View("Edit", model);
}
[HttpPost]
public ActionResult Edit(IdentityRoleView model)
{
//Your logic
return RedirectToAction("Index");
}
Then View
#model MyProject.Web.Models.IdentityRoleView
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id);
<div>
Role name
</div>
<p>
#Html.TextBoxFor(model => model.Name)
</p>
for (int i = 0; i < Model.Users.Count(); i++)
{
#Html.TextBoxFor(model => model.Users[i].Email)
#Html.TextBoxFor(model => model.Users[i].UserName)
}
<input type="submit" value="Save" />
}
If you want that user can not edit value then make TextBoxFor readonly.
I have the following ViewModel:
public class ProjectViewModel
{
public Project Project { get; set; }
public Customer Customer { get; set; }
}
The Customer property is only used to link a new Project to the Customer, so I don't include this property in my Create view, which looks like this:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Project</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Project.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Project.Name, "", new { #class = "text-danger" })
</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>
}
When I post the form, the following method in my ProjectsController is triggered:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Project,Customer")] ProjectViewModel vm)
{
var project = Mapper.Map<Project>(vm);
if (ModelState.IsValid)
{
_db.Create(project);
return RedirectToAction("Index");
}
return View(vm);
}
This is where the unexpected behaviour occurs. Now, when I examine the vm property, the Customer property is null.
The question
How can I still keep the Customer property filled, while not using it in the view?
If you want to persist your Customer data, then you need to set all the fields as hidden elements otherwise they will be lost in the redirect.
#Html.HiddenFor(model => model.Customer.Property1) ...etc...
The short answer is that you can't. When data is posted back, the only data that is included is what is in the form on the HTML page.
Your best bet is to either use a session variable or look up the data again in the post handler, or alternatively, serialize the data to hidden fields.
To make sure the Customer property is never null when initializing ProjectViewModel, you could add a constructor to your ProjectViewModel class initializing the Customer property to a new Customer() like so:
public class ProjectViewModel
{
// Constructor
public ProjectViewModel()
{
Customer = new Customer();
}
public Customer Customer { get; set; }
}
net mvc
If created my clas project, projectUser and user
I want to create a projectUser by selecting the id from project and the id from user both in a dropdownList
My projectUser controller
// GET: /ProjectUser/Create
public ActionResult Create()
{
ViewBag.UserID = new SelectList(db.Users, "Id", "UserName");
ViewBag.ProjectID = new SelectList(db.Projects, "Id", "ProjectID");
return View();
}
// POST: /ProjectUser/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 ActionResult Create([Bind(Include="ProjectUserID")] ProjectUser projectuser)
{
if (ModelState.IsValid)
{
db.ProjectUsers.Add(projectuser);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserID = new SelectList(db.Users, "Id", "UserName", projectuser.User.Id);
ViewBag.ProjectID = new SelectList(db.Projects, "Id", "ProjectID", projectuser.Projects.ProjectID);
return View(projectuser);
}
My view
<div class="form-horizontal">
<h4>ProjectUser</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Projects, "Kies project", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ProjectID", String.Empty)
#Html.ValidationMessageFor(model => model.Projects)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.User, "Kies user", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Id", String.Empty)
#Html.ValidationMessageFor(model => model.User)
</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>
my projectUser class
public class ProjectUser
{
public int ProjectUserID { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual Project Projects { get; set; }
}
You should have ApplicationUserId and ProjectId in your ProjectUser class.
Then you could simply add a ProjectUser by just binding the ids.
I am trying to do an edit without scarffolding and it is working the problem is that it cannot pick up the id or primary key of a record i am editing. Below is my code from an service repository until my view as i am using repository pattern.
here is my declaration in INTERFACE SERVICE REPOSITORY
void Edit(DonataryKind model);
here is my code/method in SERVICE REPOSITORY
public void Edit(DonataryKind model)
{
_donataryKindyRepository.Update(model);
}
here is my declaration in INTERFACE BUSINESS LOGIC
void Edit(DonataryKindView model);
here is my code/method in BUSINESS LOGIC
public void Edit(DonataryKindView model)
{
using (var donRepo = new DonataryKindRepository())
{
var objDon = donRepo.GetById(model.KindID);
if (objDon != null)
{
objDon.KindName = model.KindName;
donRepo.Edit(objDon);
donRepo.Save();
}
}
}
My code of edit in my CONTROLLER
[HttpGet]
public ActionResult Edit(int id)
{
var objdonk= new DonataryKindLogic().GetById(id);
return View(objdonk);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, DonataryKindView model)
{
if (ModelState.IsValid)
{
try
{
var objDonK = new DonataryKindLogic();
objDonK.Edit(model);
return RedirectToAction("Index");
}
catch (Exception e)
{
ModelState.AddModelError(string.Empty, "Error! " + e.Message);
}
}
return View(model);
}
My code VIEW of edit
#model Sunday.Model.DonataryKindView
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DonataryKindView</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.KindID)
<div class="form-group">
#Html.LabelFor(model => model.KindName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.KindName)
#Html.ValidationMessageFor(model => model.KindName)
</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>
Why don`t you just pick it from the model in the edit method ?
model.KindID
I'm completely puzzled how this simple concept doesn't seem to work. I have a ViewModel, a Controller and a View...
This is my basic ViewModel
public class CreateProfessionalEducationViewModel
{
public Models.ProfessionalEducation ProfessionalEducation;
public int ConsultantId;
public CreateProfessionalEducationViewModel() {} // parameterless constr
}
These are my 2 controller methods
public ActionResult Create(int id)
{
var viewModel = new CreateProfessionalEducationViewModel
{
ConsultantId = id
};
return View(viewModel);
}
//
// POST: /ProfessionalEducation/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateProfessionalEducationViewModel collection)
{
if (ModelState.IsValid)
{
//db.ProfessionalEducations.Add(professionaleducation);
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
and my view...
#model Axxes_Virtual_Resume_MVC.ViewModel.ProfessionalEducation.CreateProfessionalEducationViewModel
#using (Html.BeginForm("Create", "ProfessionalEducation")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ProfessionalEducation</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ProfessionalEducation.Year)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProfessionalEducation.Year)
#Html.ValidationMessageFor(model => model.ProfessionalEducation.Year)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ProfessionalEducation.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProfessionalEducation.Title)
#Html.ValidationMessageFor(model => model.ProfessionalEducation.Title)
</div>
#Html.HiddenFor(model => model.ConsultantId)
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
For some weird reason my ConsultantId is 0 and ProfessionalEducation is null in my ViewModel when posted back to the HttpPost Create method in my controller.
This should be so basic, what am I forgetting?
Thanks in advance!!
The main problem is that, the loony that is the DefaultModelBinder (what reconstruct viewmodels from an HttpFormCollection) works on properties not variables.
And if I'm not mistaken, I do believe the default model binder don't work so well if you use inner objects like that in your Viewmodel and access their properties directly in the View.
Try something like that :
public class CreateProfessionalEducationViewModel
{
public int ConsultantId { get; set; }
public int ProfessionalEducationYear { get; set; }
public string ProfessionalEducationTitle { get; set; }
}
And for the view of course
#model Axxes_Virtual_Resume_MVC.ViewModel.ProfessionalEducation.CreateProfessionalEducationViewModel
#using (Html.BeginForm("Create", "ProfessionalEducation")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ProfessionalEducation</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ProfessionalEducationYear )
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProfessionalEducationYear )
#Html.ValidationMessageFor(model => model.ProfessionalEducationYear )
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ProfessionalEducationTitle)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProfessionalEducationTitle)
#Html.ValidationMessageFor(model => model.ProfessionalEducationTitle)
</div>
#Html.HiddenFor(model => model.ConsultantId)
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
As a rule try to avoid referencing Model objects in your ViewModels (but that's just a design rule)
Using properties instead of fields in my ViewModel fixed it for me.
Thanks to anyone for their effort.
The name attribute of the tags must be "ProfessionalEducation.Year" or "ProfessionalEducation.Title" in order that the default binding to work.
So use this:
#Html.TextBox("ProfessionalEducation.Year")
instead of
#Html.EditorFor(model => model.ProfessionalEducation.Year)
and the same for Title property.
You can read more about ASP.NET MVC Model Binding in this useful post:
http://dotnetslackers.com/articles/aspnet/Understanding-ASP-NET-MVC-Model-Binding.aspx