views:

33

answers:

4

Given the following model which has a name, url, and an arbitrary list of keywords (I want the user to add a series of keywords) ...

public class Picture
{
  public Picture()
  {
    keywords = new List<string>();
  }
  public string name {get;set:}
  public string url {get;set;}
  public List<string> keywords{get;set;}
}

... and the following action in my controller ...

[HttpPost]
public ActionResult Edit(FormCollection fc)
{
  if (ModelState.IsValid)
  {
    // do stuff
  }
  return View(ModelManager.Picture);
}

In the FormCollection I have the following field

fc["keywords"] = "keyword1,keyword2,keyword3"

And I then create a Picture object based on the form collection.

However, I would prefer to use a strongly-typed action such as

[HttpPost]
public ActionResult Edit(Picture p)

But in this approach, my p.keywords property is always empty. Is there some way to help the framework recreate my p.keywords property before it hits my controller's action method?

A: 

Are your keywords seperate text boxes? If so, create an inputs like this and they will be populated by the model binder.

<input name="keywords[0]" type="text">
<input name="keywords[1]" type="text">
<input name="keywords[2]" type="text">
amarsuperstar
I updated the question, but the list of keywords is arbitrary and not fixed to just 3.
yamspog
The number of inputs you have have is not limited, they behave just like an array. Have a look at this article, it may help you: http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
amarsuperstar
+1  A: 

I thought an Editor Template might work here, but I don't think there is a way to model bind a nested IEnumerable view model member. Your fastest bet may be handling it directly with FormCollection and some string parsing magic. Otherwise, if you have to strongly-type this, maybe a custom model binder like this could help if you can control your keyword element id's:

public class PictureKeywordBinder : IModelBinder
{
    public object GetValue(ControllerContext controllerContext,
        string modelName, Type modelType,
        ModelStateDictionary modelState)
    {
        Picture picture = new Picture();
        //set name, url, other paramaters here

        foreach(var item in Request.Form.Keys)
        {
            if (item.StartsWith("keyword"))
            {
                picture.keywords.Add(Request.Form[item]);
            }
        }

        //add any errors to model here

        return picture;
    }
}

Maybe the keyword id's could be setup in a partial view passed the sub model from your parent view:

<% Html.RenderPartial("PictureKeywords", Model.keywords);
David
A: 

The way I got around this, is to use a hidden input to store the csv string of items, in your case, keywords.

I then hooked into the form submit event (using jQuery) and appended the inputs to form the csv string, which is then stored in the hidden input. This hidden input was strongly typed to a property on my model.

It's a little clunky, but if you have a dynamic number of possible keywords then this works quite well (except if JS is disabled of course)

Alastair Pitts
A: 

In what way you are expecting the user to add more keywords? In the form comma separated values(CSV) or by dynamically adding textboxes?

Based on your requirement, i have two solutions with me.

Siva Gopal