1

I am currently using the EntityFramework to bind my ASP.NET MVC project to a MySQL database and one of my entities, Product, has an Images property containing a collection of ProductImages. I have built a form to allow the user to modify a given Product and this form includes fields for editing all of the images associated to that Product as well. After reading Phil Haack's and Dan Miser's posts on the matter I have a decent idea of what needs to happen, but I can't seem to make it work for some reason...

Here is my Product form:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<KryptonCMS.Models.Product>" %>
<%@ Import Namespace="KryptonCMS.Core" %>
<%@ Import Namespace="KryptonCMS.Models.ViewModels" %>

<% using (Html.BeginForm())
   {%>

        <ul class="gallery">
            <%
                var index = 0;
                foreach (var image in Model.ImageList.OrderBy(p => p.Order))
                {
            %>
            <li>
                <% Html.RenderPartial("ProductImageForm", image, new ViewDataDictionary(ViewData) { { "index", index } }); %>
            </li>
            <%
                index++;
                }
            %>
        </ul>

    <p>
        <input type="submit" name="btnSave" value="Save" />
        <input type="submit" name="btnCancel" value="Cancel" />
    </p>
<% } %>

And here is the definition for ProductImageForm:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<KryptonCMS.Models.ProductImage>" %>
<%@ Import Namespace="KryptonCMS.Core" %>
<div>
    <%
        var fieldPrefix = string.Format("images[{0}]", ViewData["index"]); %>
    <%=Html.Hidden(fieldPrefix + "ID", Model.ID) %>
    <img src="<%=UtilityManager.GetProductImagePath(Model.Product.ID, Model.FileName, true) %>"
        alt="" /><br />
    <label for="Description">
        Description:</label>
    <%=Html.TextBox(fieldPrefix + "Description", Model.Description) %><br />
    <label for="Order">
        Order:</label>
    <%=Html.TextBox(fieldPrefix + "Order", Model.Order)%><br />
</div>

And finally my ProductsController actions:

    public ActionResult Edit(int id)
    {
        var product = productsRepository.GetProduct(id);

        if (product == null)
            return View("NotFound", new MasterViewModel());

        // else
        return View(ContentViewModel.Create(product));
    }

    [AcceptVerbs(HttpVerbs.Post), ValidateInput(false)]
    public ActionResult Edit(int id, FormCollection formCollection)
    {
        var product = productsRepository.GetProduct(id);

        if (formCollection["btnSave"] != null)
        {
            if (TryUpdateModel(product) && TryUpdateModel(product.Images, "images"))
            {
                productsRepository.Save();

                return RedirectToAction("Details", new { id = product.ID });
            }
            return View(ContentViewModel.Create(product));
        }

        // else
        return RedirectToAction("Details", new { id = product.ID });
    }

The HTML output for a single ProductImageForm looks like this:

<div>
    <input id="images[0]ID" name="images[0]ID" type="hidden" value="1" />
    <img src="/Content/ProductGallery/3/thumbs/car1.jpg"
        alt="" /><br />
    <label for="Description">
        Description:</label>
    <input id="images[0]Description" name="images[0]Description" type="text" value="FAST CAR" /><br />
    <label for="Order">

        Order:</label>
    <input id="images[0]Order" name="images[0]Order" type="text" value="1" /><br />
</div>

I have tried all sorts of methods of reorganizing my form including taking the Image collection out of the Product form and placing it in its own (which I really don't want to do), but nothing is working. Is there something blatatently wrong with my approach here?

1 Answer 1

4

You are missing dots in inputs' names:

<%= Html.Hidden(fieldPrefix + ".ID", Model.ID) %>
<%= Html.TextBox(fieldPrefix + ".Description", Model.Description) %>
<%= Html.TextBox(fieldPrefix + ".Order", Model.Order) %>

Check this blog post: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

Sign up to request clarification or add additional context in comments.

12 Comments

I just found that myself. Been ripping my hair out for days over a stinkin period. :(
If I try to UpdateModel on the product.Images it doesn't automatically use the ProductImage collection returned by the form, what would be the cleanest way to replace the Image collection on my product with the one returned in the form?
Try to change the field prefix to product.images[{0}]
Have you tested that? Calling TryUpdateModel(product.Images) after changing the prefix has no effect.
I did not tested that. I mean calling only TryUpdateModel(product)
|

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.