Geekatron
Geekatron

Reputation: 114

Using Editor Templates to Display Multiple Forms

My question is very similar to this one. The application I'm developing is written in MVC 3 and Razor. It lets its users select items from a store and send each one to a different address.

Here are my ViewModels:

public class DeliveryDetailsViewModel
{
    public FromDetailsViewModel From { get; set; }
    public IList<ToDetailsViewModel> To { get; set; }
}

public class DetailsViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}

public class FromDetailsViewModel : DetailsViewModel
{
    public string StreetAddress { get; set; }
    public string Suburb { get; set; }
    public string Postcode { get; set; }
}

public class ToDetailsViewModel : DetailsViewModel
{
    public string Message { get; set; }
}

My View is similar to below.

@model Store.ViewModels.DeliveryDetailsViewModel

@Html.EditorFor(m => m.From)

@Html.EditorFor(m => m.To)

My intention is that a collection of forms (one per item in their shopping cart) will be displayed to enable the user to type different delivery details in. Each form has its own submit button.

The editor template which renders the "To" form is as follows:

@model Store.ViewModels.ToDetailsViewModel

@using (Html.BeginForm("ConfirmTo", "Delivery"))
{
    @Html.TextBoxFor(m => m.FirstName)
    @Html.TextBoxFor(m => m.LastName)
    @Html.TextBoxFor(m => m.Email)
    @Html.TextBoxFor(m => m.Message)

    <input type="submit" value="Confirm" />
}

My controller:

public class DeliveryController : Controller
{
    public ActionResult Index()
    {
        var model = new DeliveryDetailsViewModel();
        model.From = new FromDetailsViewModel();
        model.To = new List<ToDetailsViewModel>();
        return View(model);
    }

    public ActionResult ConfirmTo(ToDetailsViewModel toDetails)
    {
        // Save to database.
    }
}

I have a couple of problems:

  1. The "to" editor template isn't rendering anything (though it used to). It cites that the model types don't match (i.e., ToDetailsViewModel isn't the same as List<ToDetailsViewModel>), even though I thought editor templates were supposed to append indices to input field names to enable proper binding.

  2. When clicking Confirm and submitting the first form in the To list the controller receives the view model with the correct bindings. Submitting any of the following forms (with index 1 or greater) calls the ConfirmTo action and passes a ToDetailsViewModel which is null.

Any help would be appreciated, and if you'd like more information about the problem I'm having or the code I'm using, please don't hesitate to ask.

Upvotes: 0

Views: 748

Answers (2)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038710

1) The "to" editor template isn't rendering anything

In your controller action you didn't put anything in the list. You just instantiated it. So put some elements:

model.To = new List<ToDetailsViewModel>();
model.To.Add(new ToDetailsViewModel());
model.To.Add(new ToDetailsViewModel());
...

2) When clicking Confirm and submitting the first form in the To list the controller receives the view model with the correct bindings. Submitting any of the following forms (with index 1 or greater) calls the ConfirmTo action and passes a ToDetailsViewModel which is null.

I would be surprised if this works even for the first element because the input fields currently do not have correct names. They are all prefixed with To[someIndex] whereas your ConfirmTo expects a flat model, not a collection.

So You could set the prefix to an empty string so that correct input elements are generated in your ~/Views/Shared/EditorTemplates/ToDetailsViewModel.cshtml editor template:

@model ToDetailsViewModel
@{
    ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
@using (Html.BeginForm("ConfirmTo", "Home"))
{
    @Html.TextBoxFor(m => m.FirstName)
    @Html.TextBoxFor(m => m.LastName)
    @Html.TextBoxFor(m => m.Email)
    @Html.TextBoxFor(m => m.Message)

    <input type="submit" value="Confirm" />
}

Upvotes: 1

Jayanga
Jayanga

Reputation: 887

1) have you try this, because your view model has

public IList<ToDetailsViewModel> To { get; set; }

To is a list therefore your editor template should have

@model IEnumerable<Store.ViewModels.ToDetailsViewModel>

and the template should use a foreach

@foreach(model in Model){}

Upvotes: 0

Related Questions