Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewModels with SelectList Design Decison

I have created a viewmodel

public VMPosition
{
        public VMPosition(){}//for model binder  
        public VMPosition(int EmployeeID)
        {
            PositionStatusList = new SelectList(_repo.getStatuses);
            //populate other properties
        }
        public int CurrentPositionID { get; set; }
        public int EmployeeID { get; set; }
        public int CurrentPositionHistoryID { get; set; }
        public bool AddingNew { get; set; }
        public bool ClosingCurrent { get; set; }
        public string CurrentPosition { get; set; }
        public DateTime CurrentPositionStartDate { get; set; }
        public string ReasonForDeparture { get; set; }
        public SelectList PositionStatusList { get; set; }

}

My GET ActionResult is defined as

public ActionResult UpdatePosition(int id)
{
     return View(new VMPosition(id)); 
} 

My POST actionresult is defined as

 public ActionResult UpdatePosition(int id, VMPosition Position)
    {
        if(ModelState.IsValid){
             Position Current = new Position{Position.Title etc..}
             //save to db
             return redirectToAction("someAction");
         }
         return View(Position);//here is the problem
    }

My SelectList is populated in a constructor that accepts one parameter. Modelbinder cannot and should not call the constructor if the modelstate is invalid. I will have to return the View with model object (which in this case don't contain SelectList value). How can handle this scenario when using view Models.

I can manually populate these values in actionresult but that will violate the DRY principle. However, for the purposes of this question, I'd like help addressing the bigger design question.

like image 921
Muhammad Adeel Zahid Avatar asked Dec 16 '22 19:12

Muhammad Adeel Zahid


2 Answers

Why not follow the convention which I reckon the majority of people use? You have coupled your ViewModel to your repo which I would also recommend changing. By putting the repo.GetStatuses inside your Controller/Action is simple and it works. I also prefer putting the SelectList inside my view and have the ViewModel house the list of items - but that's my personal preference. You can then clearly see/understand what type of objects your ViewModel deals with. DRY is a principle not a requirement.

ViewModel

public VMPosition
{
    public int StatusId { get; set; }
    public IList<Status> StatusList { get; set; }
}

Controller

public ActionResult UpdatePosition(int id)
{
    var model = new VMPosition(id);
    model.StatusList = _repo.getStatuses;
    return View(model);
}

public ActionResult UpdatePosition(int id, VMPosition Position)
{
    if(!ModelState.IsValid)
    {
        Position.StatusList = _repo.getStatuses;
        return View(Position);
    }
    ...
}

View

<%= Html.DropDownListFor(m => m.StatusId, new SelectList(Model.StatusList)...

Edit - Refactor PopulateSelectLists

public ActionResult UpdatePosition(int id)
{
    var model = new VMPosition(id);
    PopulateSelectLists(model);
    return View(model);
}

public ActionResult UpdatePosition(int id, VMPosition Position)
{
    if(!ModelState.IsValid)
    {
        PopulateSelectLists(Position);
        return View(Position);
    }
    ...
}

private void PopulateSelectLists(VMPosition Position)
{
    Position.StatusList = _repo.GetStatuses;
    Position.OtherSelectList = ...
    ...
}
like image 71
David Avatar answered Jan 03 '23 15:01

David


When dealing with dropdowns in my viewmodels, I usually have a single property associated with the selected list item's value and I have a property that returns a list of selectlistitems. Then, I use Html.DropDownListFor(m => m.ValueProperty, Model.DropDownValues) to render the dropdown.

I imagine in your scenario, you don't have a value corresponding to the selected listitem's value?

Edit: Here's an example from one of my apps...

public class MyVM
{
  public int MyObjectId { get; set; }

  public List<SelectListItem> MyObjectList
  {
    get
    {
      List<SelectListItem> list = (from o in MyObjects select new SelectListItem 
        { Value = o.ObjectId.ToString(), Text = o.ObjectName }).ToList();
      list.Insert(0, new SelectListItem 
        { Value = "0", Text = "[Select an object]" });
      return list;
    }
  }
}

<%: Html.DropDownListFor(m => m.MyObjectId, Model.MyObjectList)%>

You might have noticed the LINQ query that populates the list. In this example, I have a list (MyObjects) that was already populated by AutoMapper. You could simply return a static list if you prefer.

like image 30
Mayo Avatar answered Jan 03 '23 17:01

Mayo