Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 3 Model-binding and form fields

I have an entity called Domain.Models.BlogPost which contains the following properties:

  • PostID
  • Title
  • Author
  • PostedDate
  • Body

I also have an entity called Domain.Models.PostComment which contains the following properties:

  • CommentID
  • PostID
  • Author
  • Email
  • Website
  • Body

BlogPost contains many PostComments. A one to many relationship.

Now I have a view like this (separated comment form from blog post code via html comment):

@model Domain.Models.BlogPost
@using Domain.Models;
@{
    ViewBag.Title = "Post";
}
<div class="postTitle">@Model.Title</div>
<div class="subInfo">
    Posted by @Model.Author on @Model.PostedDate.ToString("D")
</div>
<div class="postBody">
    @Html.Markdown(Model.Body)
</div>
<br />
@Model.PostComments.Count Comment(s).
<div class="comments">
@foreach (PostComment postComment in Model.PostComments)
{
    Html.RenderPartial("PostComment", postComment);
}
</div>

<!-- BELOW IS THE ADD COMMENT FORM -->

<div id="addComment">
@using (Html.BeginForm("AddComment", "Blog"))
{
    <text>
    @Html.Hidden("PostID", Model.PostID)<br />
    Name: @Html.TextBox("Author")<br />
    Email: @Html.TextBox("Email")<br />
    Website: @Html.TextBox("Website")<br />
    Body: @Html.TextArea("Body")<br />
    <input type="submit" value = "Add Comment" />
    </text>
}
</div>
@Html.ActionLink("Add Comment", "AddComment")

The problem is that because the comment form uses @Html.TextBox("Author") and @Html.TextBox("Body"), they are populated with data from the model, which also contains properties Author and Body. Any suggestions on how to fix this so these fields don't get values put in them when the page loads?

I also tried creating a BlogPostViewModel and setting that as the model of the view and assigning the BlogPost property with my actual model:

public class BlogPostViewModel
{
    public BlogPost BlogPost { get; set; }
    public PostComment NewComment { get; set; }
}

Then I did @Html.TextBoxFor(x => x.NewComment.Author) but when the form posted to this action method:

public ActionResult AddComment(PostComment postComment)
{
    // ...
}

postComment did not bind to the form values :/

like image 782
Chev Avatar asked Mar 17 '11 16:03

Chev


2 Answers

You could either rename the fields in the AddComment section to something that does not collide with the properties named in the Model, or you could override the value in the view using a different overload of Html.TextBox This overload of TextBox takes a value:

value (Type: System.Object)
The value of the text input element. If this value is null, the value of the element is retrieved from the ViewDataDictionary object. If no value exists there, the value is retrieved from the ModelStateDictionary object.


UPDATE: Since you added "NewComment" as a property and resolved the property naming collision that way, all that you need to do to bind a PostComment rather than the whole view model on POST to the action, is to instruct the model binder that a prefix will be used. This is done using the BindAttribute.

public ActionResult AddComment([Bind(Prefix = "NewComment")] PostComment postComment)
like image 126
Adam Price Avatar answered Nov 11 '22 15:11

Adam Price


Use ASP NET MVC templates so you have full control over what gets populated and it is type-safe.

So you would create an .ascx template which takes a strongly typed Comment. In your model, you leave an empty one in there.

like image 1
Aliostad Avatar answered Nov 11 '22 15:11

Aliostad