cpaulus
cpaulus

Reputation: 284

Default value for ViewModel

I'm making a simple Search page in MVC with some filters in it. The filters are represented by properties in my ViewModel. My ViewModel is binded to a GET form in the cshtml so my filter will appears in the querystrings and the user will be able to bookmark his search.

What I want to do is to assign a default value to some of my filters.

My (simplified) ViewModel :

public class SearchViewModel
{
    //Filter I want to set a default value to
    public OrganizationType? OrganizationType {get; set;}

    //Results of the search
    public IEnumerable<ItemViewModel> Items {get; set;}
}

I'd like to set a default value for OrganizationType. I can't simply set it in the constructor of SearchViewModel because it depends on the current user :

public void InitViewModel(SearchViewModel vm)
{
    vm.OrganizationType = _someLogic.GetDefaultValue(_currentUser);
}

First solution was simply to check if OrganizationType is null, then assign a default value :

public ActionResult Search(SearchViewModel vm)
{
    if(vm.OrganizationType == null)
        vm.OrganizationType = _someLogic.GetDefaultValue(_currentUser);

    return View(vm);
}

But this solution doesn't work as a null value corresponds to an empty filter and it's a choice that the user can make. So I can't override it.

The second solution I tried was to specify that the default value of the controller should be null in the Search action :

public ActionResult Search(SearchViewModel vm = null)
{
    if (vm == null) 
    {
        vm = new SearchViewModel();
        InitViewModel(vm);
    }       
    ...

    return View(vm);
}

But in practice, the variable vm is never null, so the default values are never setted.

I also tried having two Action, one wihout a ViewModel where I instanciate a new ViewModel with the default values and then call the second action :

public ActionResult Search()
{
    var vm = new SearchViewModel();
    InitViewModel(vm);  

    //Simply call the second action with the initizalied ViewModel          
    return Search(vm);
}   

public ActionResult Search(SearchViewModel vm)
{       
    ...
    return View(vm);
}

But it doesn't work because there is now an ambiguity between the two action, and asp.net doesn't know which one to choose.

So in summary, I'd like to find a way to set a default value for a ViewModel, without setting it in the constructor and overriding user choices.

Another way to say it, how can I distinguish an "empty" ViewModel from one where some values are binded from the form.

Any idea ?

Upvotes: 0

Views: 1334

Answers (2)

Chris Pratt
Chris Pratt

Reputation: 239210

The logic of what you're doing is a little iffy. Generally speaking, if a value is nullable then null is the default value. However, it seems that you're trying to make a distinction here between whether the value is null because it's not set or null because the user explicitly set it to null. This type of semantic variance is usually a bad idea. If null has a meaning, then it should always carry that meaning. Otherwise, your code becomes more confusing and bugs are generally introduced as a result.

That said, you can't count on ModelState having no items. I've honestly never played around with ModelState enough in scenarios where there's not post data, but it's possible there's some scenario where there's no post data and yet ModelState may have items. Even if there isn't, this is an implementation detail. What if Microsoft does an update that adds items to ModelState in situations where it previously had none. Then, your code breaks with no obvious reason why.

The only thing you can really count on here is whether the request method is GET or POST. In the GET version of your action, you can reasonably assume that the user has made no modifications. Therefore, in this scenario, you can simply set the value to whatever you like without concern.

In the POST version of your action, the user has made some sort of modification. However, at this point, there is no way to distinguish any more whether the value is null because it is or because the user explicitly wanted it to be. Therefore, you must respect the value as-is.

Upvotes: 0

cpaulus
cpaulus

Reputation: 284

Ok I think I found a solution to my own problem...

I can use the ModelState property of the controler to check it the ViewModel is empty or was binded from the form :

public ActionResult Search(SearchViewModel vm = null)
{

    if (ModelState.Count == 0) 
    {        
        InitViewModel(vm);
    }       
    ...
    return View(vm);
}

So if ModelState.Count equals to 0 it means that user didn't change any filters. So the form is empty and we can bind our default values. As soon as the user will change one of the filters or submit the request, the ModelState.Count will be greater than 0 so we shouldn't set the default value. Otherwise we would override an user choice.

Upvotes: 1

Related Questions