开发者

Repopulating certain property values if ModelState.IsValid is false

I have the following action method (partial code):

[HttpPost]
public ActionResult Create(EditGrantApplicationViewModel editGrantApplicationViewModel)
{
   if (!ModelState.IsValid)
   {
      return View("Create", editGrantApplicationViewModel);
   }

   return View("Index");
}

EditGrantApplicationViewModel looks like this (partial code):

public class EditGrantApplicationViewModel
{
   public IEnumerable<Title> Titles { get; set; }
   public int TitleId { get; set; }
   public IEnumerable<Bank> Banks { get; set; }
   public int BankId { get; set; }
   public IEnumerable<AccountType> AccountTypes { get; set; }
   public int AccountTypeId { get; set; }
}

When this Create view is requested for the first time I would populate Titles in my service layer and just return an instance of EditGrantApplicationViewModel like this:

public ActionResult Create()
{
   EditGrantApplicationViewModel editGrantApplicationViewModel = grantApplicationService.CreateEditGrantApplicationViewModel();

   return View(editGrantApplicationViewModel);
}

The CreateEditGrantApplicationViewModel in my service layer:

public EditGrantApplicationViewModel CreateEditGrantApplicationViewModel()
{
   EditGrantApplicationViewModel editGrantApplicationViewModel = new EditGrantApplicationViewModel
   {
      Titles = titleRepository
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name)
   };

   return editGrantApplicationViewModel;
}

When I click the submit button it will go into the post action Create method. It receives an editGrantApplicationViewModel parameter of type EditGrantApplicationViewModel. Why is the Titles property set to null? I thought it would retain it values?

Now lets say that there is an error, ModelState.IsValid is false. So this means that I am going to have to repopulate the Titles property. Given the property values that have been set in the form in editGrantApplicationViewModel, how would I populate the Titles property now? I'm assuming that I would need to have another method in my service layers that populates it? What is the best way to do this?

Any source code would be appreciated.

UPDATE 2011-04-11

On my view I have 3 dropdowns. Titles, banks, and account types. This is why I have 3 lists in my view model. I have a service class for each to handle inserts, updates, and getting items. For example, in my bank service class I would have Insert, Update, GetAll, GetB开发者_如何学GoyId, etc methods relating to bank. I would have similar in the title and account type services.

This is how I currently have it in my controller class:

private IGrantApplicationService grantApplicationService;
private ITitleService titleService;
private IBankService bankService;
private IAccountTypeService accountTypeService;

public GrantApplicationController(IGrantApplicationService grantApplicationService, ITitleService titleService, IBankService bankService, IAccountTypeService accountTypeService)
{
   this.grantApplicationService = grantApplicationService;
   this.titleService = titleService;
   this.bankService = bankService;
   this.accountTypeService = accountTypeService;
}

public ActionResult Create()
{
   EditGrantApplicationViewModel editGrantApplicationViewModel = new EditGrantApplicationViewModel
   {
      // Populate the dropdown lists
      Titles = titleService
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name),
      Banks = bankService
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name),
      AccountTypes = accountTypeService
         .GetAll()
         .Where(x => x.Active)
         .OrderBy(x => x.Name)
   };

   return View(editGrantApplicationViewModel);
}

We spoke a while back, you said that it is preferable to have one service for a controller. In my case I need to populate 3 lists from 3 different database tables. Can you please provide some code as to how you would have done it. If more details is needed please let me know.


A service layer shouldn't return view models, it should work with models. So have your service layer return those titles:

public IEnumerable<Title> GetTitles()
{
    return titleRepository
        .GetAll()
        .Where(x => x.Active)
        .OrderBy(x => x.Name)
    };
}

and then leave the responsibility of instantiating the view model to the controller:

public ActionResult Create()
{
    var model = new EditGrantApplicationViewModel 
    {
        Titles = grantApplicationService.GetTitles()
    };
    return View(model);
}

[HttpPost]
public ActionResult Create(EditGrantApplicationViewModel model)
{
    if (!ModelState.IsValid)
    {
        // Reload the Titles as we are redisplaying the same view
        // and they were not part of the view model that was submitted
        model.Titles = grantApplicationService.GetTitles();
        return View("Create", model);
    }
    return View("Index");
}


Since mvc is stateless (for the most part as you can use TempData and session to maintain some state). After the initial request and display the titles property is essentially lost.

To get the Titles to be built back up again you need to display those values somehow on the form so the ModelBinder can rehydrate your EditGrantApplicationViewModel

You most likely will need to do something like this:

<% foreach(Title title in Model.Titles { %>
  <%: Html.HIddenFor(m => title) %>
<% } %>

note my syntax might be a bit off since I don't have visual studio on my machine.

Also I am not sure how the ModelBinder will handle your IEnumberable<T> you might have to change that to List<T> so the ModelBinder can build up the type correctly.

Additional information on how to bind to your Titles list.

  1. Post from Phil Haack about Model Binding To A List
  2. A slightly smiler question about mvc and IEnumerable<T>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜