MVC viewmodel Edit shows nothing after Httppost

73 views Asked by At

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")
}

EmployeeViewModel

1

There are 1 answers

4
Shyju On BEST ANSWER

Your current code(in the edit view) will generate input markup like below.

<input class="form-control text-box single-line" 
        id="item_DepartmentName" name="item.DepartmentName" type="text" value="SomeName" />

See the name attribute value. It is "item.DepartmentName". Your HttpPost action method parameter is the EmployeeViewModel 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.

<input name="DepartmentName" type="text" value="Some value" />

You can explcitily specify the name attribute alue when calling the html helpers. For example, with the Html.TextBoxFor helper

 @Html.TextBoxFor(i => item.DepartmentName, 
                               new  { @class = "form-control", NAME = "DepartmentName"  })

Now when you submit the form, Model binder will be able to map the value of this form field to the DepartmentName property of your EmployeeViewModel object.