VJAI
VJAI

Reputation: 32758

ASP.NET MVC model binding child objects

I'm working on ASP.NET MVC 3 application. I have a Comment class that encapsulates a Post class (each comment is associated with a post in the blog), there is an action method for edit a comment as below,

    [HttpPost]
    [Authorize(Users = admin)]
    public string EditComment(Comment comment)
    {
        //update the comment by calling NHiberante repository methods
    }

From javascript I'm posting only the comment object values like comment id, description to the server, so when binding I see the Post property inside the comment object as null and when I do the ModelState.IsValid i'm getting empty errors. My question is how I can bind the post object to the comment? I can pass the post id to the server along with the other values.

Thanks Vijaya Anand

Upvotes: 3

Views: 5633

Answers (2)

Robert Koritnik
Robert Koritnik

Reputation: 105029

Provide additional fields

If you want your post instance to be pre-populated as well, you will have to include its required fields as well.

I suspect you're having a post view where user has a form that displays comment fields so they can post a comment on the post.

suppose you have classes as:

public class Post
{
    public int Id { get; set; }

    [Required]
    public string Title { get; set; }

    public string Content { get; set; }
}

public class Comment
{
    public int Id { get; set; }

    [Required]
    public string Author { get; set; }

    [Required]
    public string Content { get; set; }

    public Post Post { get; set; }
}

Usual browser POST

What you would have to do is to add post fields (of hidden type) to your form. I don't know how you created your form whether you're using strong type views or not... Anyway. Your inputs would have to have names:

  • Post.Id (for the sake of attaching new comment to a particular post
  • Post.Title (because I've set it as required, but you could put in here whatever dummy text if all you'll be using is Post.Id; Title just needs to be set so your model validation won't fail)
  • Author - visible (this is comment author)
  • Content - visible (this is comment content)

Whether you're creating these fields using Html helpers or manually doesn't really matter. Default model binder will be able to create post object instance and your model validation won't fail.

Ajax POST

If you're posting your comments using Ajax you can do the same as described in the previous example and simply use jQuery's .serialize() function that will serialize form fields when calling $.ajax().

But you could as well programmaticaly collect all fields using Javascript and post that data to server just as well. You could simply create a JSON object:

var comment = {
    Author: $("#Author").val(),
    Content: $("#Content").val(),
    Post: {
        Id: $("#Post_Id").val(),
        Title: $("#Post_Title").val()
    }
};

$.ajax({
    url: "someURL",
    type: "POST",
    data: $.toDictionary(comment),
    success: function(data) {
        // probably get a partial view with comment you can append it to comments
    },
    error: function(xhr, status, err) {
        // process error
    }
});

There are 2 things that are being used here and need some explanation:

  1. Ajax error handling can also consume invalid model state errors (model validation failing) and can be accomplished this way.

  2. Converting a complex (multi-level) JSON object has been done by using a special $.toDictionary() plugin that can be found here and provides the ability to simply consume complex JSON objects that are understood by Asp.net MVC default model binder. It wors with dates and lists as well.

Upvotes: 4

Jonas Stensved
Jonas Stensved

Reputation: 15286

I think your looking for the TryUpdateModel or UpdateModel methods on the Controller. You can bind either by passing Comment as an argument (as your doing in your example) or by calling the UpdateModel-methods.

If you're fetching from a db (as you are doing) you should use those methods to update the existing entity instead of creating a new one.

I't might look something like this:

       [HttpPost]
       [Authorize(Users = admin)]
        public string EditComment(int id, FormCollection form)
        {
               //update the comment by calling NHiberante repository methods

               // Fetch comment from DB
                var comment =  NHibernateHelper.Get<Comment>(id);

                // Update the comment from posted values
                TryUpdateModel(comment);

                // Handle binding errors etc.
                if (!ModelState.IsValid)
                {
                    // On error
                }

            // Commit to DB
            NHibernateHelper.Update<Comment>(comment);


            //Done!



        }

I'm also using NHibernate and it works great with this implementation.

Upvotes: 0

Related Questions