0

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.

2 Answers 2

1

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" />
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your help Darin, part 1 of my problem was resolved! In regards to the input field prefixes, the solution you've provided works, however it means that if I return the view and provide it with the updated model, all FirstName fields get the value I've entered instead of just the one form I've edited. (I hope I'm expressing myself well enough to be understood.)
I solved the problem by using RedirectToAction("Index") instead of returning View("Index", model). Thanks Darin!
0

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){}

5 Comments

Unfortunately I have tried that, yes. Because the "to" form appears multiple times on the page, the model binder gets confused and fills in all "FirstName" fields with the submitted input. This is because the names of the inputs are the same for each form.
Also, passing a list of view models to the EditorFor() helper is supposed to handle creating fields with differing names. For example: "To_0__FirstName" and "To_1__FirstName".
Then how about rather using a submit button just use <input type = button> and write a jQuery to get information and submit through jQuery
for that you can bind the id of the To to necessary columns
I thought of that, and it's a really good suggestion, but what if the user has JavaScript disabled?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.