views:

41

answers:

1

I have a Model that I can bind correctly for display purposes but I can't post an updated version.

I can display a page and its list of contents in a form for updating in a strongly typed view.

But when I post an updated model i.e. if I changed a pagecontents content then the posted model contains the Pages details but loses the relationship between the page and the pagecontent.

So when I get the Page for display purposes the expanded PageContents Page contains the correct deatils.

But when I update and Post PageContents contains no Page entry.

(BTW may be a red herring but I am using Fluent nHibernate)

Unfortunately I can't find what I am missing.

So here's an abridged version of my code:

public class Page
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual string Title { get; set; }

    public virtual IList<PageContent> PageContents { get; private set; }

    public Page()
    {
        PageContents = new List<PageContent>();
    }

    public virtual void AddPageContent(PageContent pageContent)
    {
        pageContent.Page = this;
        PageContents.Add(pageContent);
    } 
}


public class PageContent
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual string Content { get; set; }
    public virtual Page Page { get; set; }
}

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

Page <%= Html.LabelFor(model => model.Title) %>:
<%= Html.TextBoxFor(model => model.Title, new { @class = "wide-input" })%>
<%= Html.ValidationMessageFor(model => model.Title) %>

Page <%= Html.LabelFor(model => model.Name) %>:
<%= Html.TextBoxFor(model => model.Name, new { @class = "wide-input" })%>
<%= Html.ValidationMessageFor(model => model.Name) %>

<% 
    foreach (var item in Model.PageContents)
    {
%>
        <%= Html.TextAreaFor(m => item.Content) %>

<%
    } 
%>

<p><input type="submit" value="Save" /></p>

<% } %>
+1  A: 

Basically you need unique ID's for each list element. Something like

<input id="PageContents_0_Content" name="PageContents[0].Content" value="..." />

This can be done by:

<%= Html.TextBox(string.Format("PageContents[{0}].Content", i), Model.Products[i].Id) %>

This this blogpost:

http://weblogs.asp.net/nmarun/archive/2010/03/13/asp-net-mvc-2-model-binding-for-a-collection.aspx

This blogpost describes a much better solution:

http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

It uses an own extensions HTML Helper method to initialize the list bindings.

EDIT: I misunderstood this, was focused on Collections with ASP.NET MVC:

if I changed a pagecontents content then the posted model contains the Pages details but loses the relationship between the page and the pagecontent.

As far as I know MVC cant restore this relationship. If Page already contains PageContent after a PostBack then everything is "fine". Means: Your collections are posted correctly back. But MVC has no idea about you PageContent.Page reference. So you have to restore if manually.

EDIT2:

(BTW may be a red herring but I am using Fluent nHibernate)

If I interpret that correctly you are using your model directly to bind your views - right? If so, I would suggest that you create ViewModels, copying everything from your model and back. This would decouple the Model from the View. Your ViewModel could provide much more information needed by your View. See MVVM Pattern.

Arthur
@Arthur - thanks for your response - I've changed my code to the following <%= Html.TextArea(string.Format("PageContents[{0}].Content", i), Model.PageContents[i].Content) %> but the issue remains the same. When the controller receives the post Page contains a collection of PageContents that for all intents and purposes are new and they don't contain a reference back to Page.
Nicholas Murray
@Arthur - yes I'll go down the ViewModel route. I think I got caught up in some double-think!
Nicholas Murray