-1

I am having problem understanding as to why my model is not updating when I select a new value from my dropdownlist control?

Here is my model

public class UserViewModel
{
    public Users users { get; set; }                
    public IEnumerable<SelectListItem> UserRoles { get; set; }                
}

Controller

//GET
public ActionResult Edit(int id)
{
        var vm = new UserViewModel();
        vm.users = repository.GetById(id);

        vm.UserRoles = db.UserRoles.Select(
                            x => new SelectListItem
                            {
                                Selected = true,
                                Text = x.UserRoleName,
                                Value = x.UserRoleID.ToString()
                            }
            );

        if (vm == null)
        {
            return HttpNotFound();
        }
        return View(vm);
   }


[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(UserViewModel model)
{
        if(ModelState.IsValid)
        {
            db.Entry(model).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View();       
 }

And finally my View

<div class="form-group">
<label class="control-label col-md-2">User Role</label>
<div class="col-md-10">
       @Html.HiddenFor(model => model.users.UserRoleID)
       @Html.DropDownListFor(model => model.UserRoles, (IList<SelectListItem>)ViewBag.UserRoles, "-- Select One --", new { @class = "form-control" })                
     </div>
 </div>

I have stepped through the code and in the Collection can see UserRoles in the collection but I am not sure if I am passing the value correctly?

UPDATE

I have updated my POST method for updating the model

public ActionResult Edit(int id, UserViewModel model)
{
        var user = repository.GetById(id);
        if (ModelState.IsValid)
        {
            if (user != null)
            {
                user.Username = model.users.Username;
                user.Forename = model.users.Forename;
                user.Lastname = model.users.Lastname;
                user.Email = model.users.Email;
                user.Status = model.users.Status;
                user.UserRoleID = Convert.ToInt32(model.UserRoles);

                db.Entry(user).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }                
        }
        return View();         
    }

However on Submit it is giving me a Null reference exception on the dropdownlist as shown below? Now sure why?

Null reference exception

KJSR
  • 1,679
  • 6
  • 28
  • 51
  • try to comment lie `Selected = true` or set it `Selected = false` – G J Sep 26 '16 at 11:04
  • 2
    You cannot bind a ` –  Sep 26 '16 at 11:05
  • And your adding the SelectList to you model property, so use it (not `ViewBag`) and do not set the `Selected` property of `SelectListItem` - its ignored when binding to a model property (the whole point of model binding to to bind to your property, so its the value of the property which determines what is selected) –  Sep 26 '16 at 11:06
  • I agree to @StephenMuecke – G J Sep 26 '16 at 11:07
  • Thanks Guys for the feedback I shall revisit my code. However why the down vote? Please explain the reason? – KJSR Sep 26 '16 at 11:23

3 Answers3

1
(IList<SelectListItem>)ViewBag.UserRoles 

you data exist in vm.UserRoles lists not in ViewBag.UserRoles but you are attaching list using Viewbag

ViewBag.UserRoles = db.UserRoles.Select(
                            x => new SelectListItem
                            {
                                Selected = true,
                                Text = x.UserRoleName,
                                Value = x.UserRoleID.ToString()
                            }

assign the list to the view model then you will get access to the list from the page

MMM
  • 3,132
  • 3
  • 20
  • 32
  • You are correct @Midhun. I change the view to `@Html.DropDownListFor(model => model.UserRoles, Model.UserRoles, "-- Select One --", new { @class = "form-control" })` however on update I get a `Null Reference` error? – KJSR Sep 26 '16 at 11:34
  • @Kevin this may be becauseFrom ur post method you are not adding value to the list.or your index page expecting some data to view – MMM Sep 26 '16 at 11:37
  • Thanks Midhun. I have updated my Post method. However I am not sure how I need to update the list? Sorry I am confused? – KJSR Sep 26 '16 at 11:41
  • 1
    you cant update using view model you have to pass model as a parameter – MMM Sep 26 '16 at 11:46
  • 1
    refer here http://www.entityframeworktutorial.net/EntityFramework4.3/update-entity-using-dbcontext.aspx – MMM Sep 26 '16 at 11:48
  • Thanks for the reference Midhun. I have updated my post to reflect further change. However still no joy on the null exception? – KJSR Sep 26 '16 at 12:10
0

Finally solved the issue. Created a new property inside my ViewModel which will store the selected value for When posting back to Controller

public class UserViewModel
{
    public Users users { get; set; }                
    public IEnumerable<SelectListItem> UserRoles { get; set; }                    
    public string selectedRole { get; set; }
}

Made change to my View to include the new property

@Html.DropDownListFor(model => model.selectedRole, Model.UserRoles, "-- Select One --", new { @class = "form-control" })

and on Post I pass the selected value

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, UserViewModel model)
{
    var user = repository.GetById(id);
    if (ModelState.IsValid)
    {
       if (user != null)
       {
           user.Username = model.users.Username;
           user.Forename = model.users.Forename;
           user.Lastname = model.users.Lastname;
           user.Email = model.users.Email;
           user.Status = model.users.Status;
           user.UserRoleID = Convert.ToInt32(model.selectedRole);           

           db.Entry(user).State = EntityState.Modified;
           db.SaveChanges();
           return RedirectToAction("Index");
       }
       else
       {
           return View();
       }                
        return View();         
    }
KJSR
  • 1,679
  • 6
  • 28
  • 51
  • Your POST method is still not right - you need to repopulate the SelectList if you return the view (refer [this question/answer](http://stackoverflow.com/questions/34366305/the-viewdata-item-that-has-the-key-xxx-is-of-type-system-int32-but-must-be-o)). And you property `public Users users { get; set; } ` should be removed –  Sep 26 '16 at 22:29
  • Hi Stephen I require the `public Users users {get;set;}` property as I use it within my Action Result and View. – KJSR Sep 27 '16 at 08:32
  • 1
    No you do not (and should not). Your using a view model so that view model should contain the properties of `User` that you want to display in the view (view models should not contain data models) –  Sep 27 '16 at 08:34
0

I think the wrong code is in the View:

<div class="col-md-10"> @Html.HiddenFor(model => model.users.UserRoleID) @Html.DropDownListFor(model => model.UserRoles (IList<SelectListItem>)ViewBag.UserRoles, "-- Select One --", new { @class = "form-control" }) </div>

The next code should work:

<div class="col-md-10"> @Html.DropDownListFor(model => model.users.UserRolesID, new SelectList(Model.UserRoles, "Id", "Name"), new { @class = "form-control" }) </div>

where there is a direct binding between model.users.UserRolesID and the list containing user roles (I suppose that UserRoles is done as id,description)