I've had issues with viewmodels for a while and I'd like to clear something up. In my viewmodel, I can show "index" and I can add a new employee "create", but the "Edit" doesn't work.
I can show the "edit" page, do my edits (like changing the Name) but when I post back, all the data shows up as null. In the "create", after I post the insert, the controller DOES SHOW the changes (EmployeeViewModel) and inserts the record. It just doesn't show when a do the "edit".
Is this something inherent in viewmodel's or is there something else?
Here's my viewmodel class (database first):
public partial class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public Nullable<int> DepartmentId { get; set; }
public string Address { get; set; }
public virtual Department Department { get; set; }
}
public partial class Department
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Department()
{
this.Employees = new HashSet<Employee>();
}
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Employee> Employees { get; set; }
}
public class EmployeeViewModel
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public Nullable<int> DepartmentId { get; set; }
public string Address { get; set; }
public string DepartmentName { get; set; }
}
Here's my controller:
public class TestController : Controller
{
public db dContext = new db();
public ActionResult Index()
{
List<Employee> employeelist = dContext.Employees.ToList();
EmployeeViewModel employeeVM = new EmployeeViewModel();
List<EmployeeViewModel> employeeVMList = employeelist.Select(x => new EmployeeViewModel
{
Name = x.Name,
EmployeeId = x.EmployeeId,
Address = x.Address,
DepartmentId = x.DepartmentId,
DepartmentName = x.Department.DepartmentName
}).ToList();
return View(employeeVMList);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create( EmployeeViewModel employeeVM)
{
if (ModelState.IsValid)
{
Employee e = new Employee();
e.EmployeeId = employeeVM.EmployeeId;
e.Name = employeeVM.Name;
e.DepartmentId = employeeVM.DepartmentId;
e.Address = employeeVM.Address;
dContext.Employees.Add(e);
dContext.SaveChanges();
return RedirectToAction("Index");
}
return View("Index");
}
public ActionResult Edit( EmployeeViewModel em , int? id)
{
var dbEmpVM = (from e in dContext.Employees
join d in dContext.Departments
on e.DepartmentId equals d.DepartmentId
where e.EmployeeId == id
select new EmployeeViewModel
{
EmployeeId = e.EmployeeId,
DepartmentId=e.DepartmentId,
Address=e.Address,
Name=e.Name,
DepartmentName=d.DepartmentName
}).ToList();
return View( dbEmpVM );
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EmployeeViewModel model, int id)
{
string name = Request.Form["EmployeeId"];
string naaanm = model.EmployeeId.ToString();
return RedirectToAction("Index");
}
}
And here's my Edit:
@model IEnumerable<MVCTutorial.Models.EmployeeViewModel>
@{
ViewBag.Title = "Edit";
}
<h4>(EmployeeViewModel)</h4>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
foreach (var item in Model)
{
<div class="form-group">
@Html.LabelFor( i => item.EmployeeId, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(i => item.EmployeeId, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(i => item.EmployeeId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(i => item.Name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(i => item.Name, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(i => item.Name, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(i => item.DepartmentId, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(i => item.DepartmentId, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(i => item.DepartmentId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(i => item.DepartmentName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(i => item.DepartmentName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(i => item.DepartmentName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(i => item.Address, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(i => item.Address, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(i => item.Address, "", new { @class = "text-danger" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" class="btn btn-default" />
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Your current code(in the edit view) will generate input markup like below.
See the name attribute value. It is
"item.DepartmentName"
. Your HttpPost action method parameter is theEmployeeViewModel
type which does not have a property with this name! So model binder will not be able to map the form data posted from your form with this name ("item.DepartmentName"
) to any properties of your method parameter.The solution is to update your input field's name attribute value to match with the property names of your
EmployeeViewModel
. Ideally your code should generate markup like below.You can explcitily specify the
name
attribute alue when calling the html helpers. For example, with theHtml.TextBoxFor
helperNow when you submit the form, Model binder will be able to map the value of this form field to the
DepartmentName
property of yourEmployeeViewModel
object.