0

I have a viewmodel called ArticleAdmin that includes a list of checkboxes:

public class ArticleAdmin
{
    public ArticleAdmin()
    {
        TopicCheckboxes = new List<TopicCheckbox>();
    }

    ... 

    public IList<TopicCheckbox> TopicCheckboxes { get; set; }

    ...
}

ToopicCheckbox has its own viewmodel class, defined in a separate file:

public class TopicCheckbox
{
    public bool IsAssociated { get; set; }

    public string TopicName { get; set; }

    public int TopicId { get; set; }
}

This works well for passing the model into the view:
(UPDATE: this Action method is newly included for some clarity)

    public ActionResult Edit(int id)
    {
        //Get the Article entity by id:
        var articleEntity = Repository.Articles.Get<Article>(id);

        //Map the entity to the viewmodel:
        Mapper.CreateMap<Article, ArticleAdmin>();

        // 2nd mapping to populate the article's relations to topics:
        Mapper.CreateMap<TopicArticle, TopicArticleAdmin>(); 

        var articleData = Mapper.Map<Article, ArticleAdmin>(articleEntity);

        //Generate checkboxes (models) to manage associations with topics:
        foreach (var topic in Repository.Topics.List())
        {
            var topicCheckbox = new TopicCheckbox { TopicId = topic.Id, TopicName = topic.Title };

            if (Repository.TopicArticles.FindList(x => x.TopicId == topic.Id && x.ArticleId == id).Count() > 0)
                topicCheckbox.IsAssociated = true;

            //and add them to the viewmodel:
            articleData.TopicCheckboxes.Add(topicCheckbox);
        }

        return View(articleData);

    }

...all the checkboxes I expect appear in the form:

But apparently this list isn't model-binding back to the [HttpPost] "Edit" ActionMethod.

Even though the TopicCheckboxes list was populated in the form, the list is empty in the ActionMethod.

[HttpPost]
public ActionResult Edit(ArticleAdmin articleData)

... the count of articleData.TopicCheckboxes is 0.

So how do I get model-binding to work properly so that this list of checkboxes back in the ActionMethod is populated correctly on post-back?

Faust
  • 15,130
  • 9
  • 54
  • 111

2 Answers2

2

You have initialized TopicCheckBoxes, but you didn't add elements to it.

Check out this question which was answered by Haacked's article and this answer, which has a custom ModelBinder to attach lists.

Community
  • 1
  • 1
Ken D
  • 5,880
  • 2
  • 36
  • 58
  • Thanks LordCover. I'm diving into that example to see if I can understand it -- looks like I need to dig into ModelBindingContext and ControllerContext. For the moment, I just want to clarify (and I've updated my question to reflect this): I have no problem populating the model to send to the view, the problem is model-binding on the HttpPost action-method. – Faust May 04 '11 at 07:18
0

OK, I've figured it out based largely on this Question: Custom Model Binder for Complex composite objects HELP

As I now feel this is probably a duplicate question, I'll delete it, unless someone comes in over the next day or so and comments that it is otherwise useful.

The key is in setting up an array structure in the input name attributes of the checkboxes. In my case, that means each checkbox needs a series of hidden values:

<div>

    <input type = "checkbox" name="TopicCheckboxes[1].IsAssociated" value = "true"id="topic_1" checked />

    <input type = "hidden" name = "TopicCheckboxes.Index" value = "1" />
    <input type = "hidden" name="TopicCheckboxes[1].IsAssociated" value = "false" />
    <input type = "hidden" name = "TopicCheckboxes[1].TopicName" value = "test" />
    <input type = "hidden" name = "TopicCheckboxes[1].TopicId" value = "1" />
    <label for='topic_1'> test </label>
</div>

The really, really important field is the first hidden field: TopicCheckboxes.Index "which the default binder looks at for it's own use", and needs to be repeated with a different value for each checkbox.

Community
  • 1
  • 1
Faust
  • 15,130
  • 9
  • 54
  • 111