Mvc – Is it valid to let view models populate their own lists for dropdowns in mvc

asp.net-mvcmvc

I've struggled to find any discussion on this.

If you have a simple MVC page for updating a User, and you put a dropdown list on it of Country, you need to populate the dropdown list in the viewmodel. Let's call it List<Country> countries {get; set;}

If the page is posted, and there's an error like they forgot to put a User Name in, we redirect them to the page again to try again, at which point we also have to remember to repopulate the model's List<Country> before redirecting them back to the page.

Three ways to handle this that I know of:

  • In the controller, make a call to the database, e.g. viewModel.Countries = _dataAccess.GetCountries()
  • Store the list in the view bag and not in the model at all e.g. ViewBag.Countries = _dataAccess.GetCountries()
  • Put it into the view model itself, so that List<Country> {get { return _dataAccess.GetCountries(); and thus inject in a DataAccess object.

The third approach is what I am considering because then you don't need to worry about populating/repopulating the fields in the controller. But I can't find any evidence via searching that this is an acceptable method, so I fear there is a drawback I am not considering.

The third method also has a big problem in that GetCountries() is async, and you can't call async methods in properties or even in constructors it seems.

Best Answer

I think this is the main difference between MVC and MVVM approaches.

In MVC you have the controller populates the ViewModel which is essentially just the a struct of the various data the view needs

In MVVM you don't have a controller, so all the logic goes in the ViewModel, which is now a 'proper' class with methods and everything.

If you are in the ASP MVC framework, then you are constrained by the fact that the view is a webpage running on a client machine. It can't really make calls back to the viewmodel generated server side. So you might as well do the logic in the controller.

There's no obvious harm in having the controller build a ViewModel, call its methods and return the results. But it's an extra step you don't need.

Say for example, you have the page with its server side populated list of countries, populated via viewModel.GetCountries()

But you also have a client side api call which returns the list of countries.

Would you choose in your controller to do

public class UserController
{
    //MVVM
    public Action GetUserFormWithCountryDropDown
    {
        return View(new ViewModel(dataAccess)); //vm has dataAccess.GetCountries in a property
    }

    MVC
    public Action GetUserFormWithCountryDropDown
    {
        var vm= new ViewModel();
        vm.Countries = dataAccess.GetCountries()
        return View(vm);
    }
}

and

public class CountryApiController
{
    //MVVM
    public List<Country> GetCountryDropDown
    {
        var vm = new ViewModel(dataAccess)
        return vm.Countries() //calls dataaccess
    }
    //MVC
    public List<Country> GetCountryDropDown
    {
        return dataAccess.GetCountries();
    }
}

It doesn't make sense from the API to use the MVVM style because its just using a single property from the VM.

Further, you can't bind your client side commands to the server side VM. Its instanciated and discarded on every HTTP call.

The downside of the MVVM approach becomes apparent when you have a complex viewmodel and only want a small part of it to perform an action. ie.

public class CountryApiController
{
    //MVVM
    public List<Country> GetCountrys
    {
        var dataAccess = new DataAccess(countries);
        var translator = new Translator(htmlContext.Local);
        var facialRecognition = new Recogniser(User.Picture);

        var vm = new UserViewModel(
            dataAccess,
            translator,
            facialRecognition,
            ... etc
            );

        return vm.Countries() //just return a list of countries, other logic not required
        //wait do i need to duplicate this for non user related country lists?
    }

}
Related Topic