2

I was wondering if it would be possible having a "params" argument in a controller function, or something similar which would allow me to process X amount of entries in my form.

For instance, I have a form which has X amount of "name" elements, which are auto-generated through jQuery. An example of these name elements could be the following:

<input type="text" name="studentName1"></input>
<input type="text" name="studentName2"></input>
<input type="text" name="studentName3"></input>

Now, there's a different amount of student names every time, so this makes it quite complex for me to handle the form data in my controller. I had something like the following 2 examples in mind, but of course they wouldn't work in reality.

[HttpPost]
public ActionResult PostStudentNames(params string[] studentNames)

Or:

[HttpPost]
public ActionResult PostStudentNames(string[] formValues)

Can I achieve something similar to that?

jim tollan
  • 22,305
  • 4
  • 49
  • 63
Mathias Lykkegaard Lorenzen
  • 15,031
  • 23
  • 100
  • 187

3 Answers3

4

I just want to chime in with a different approach you can use for this. If it's more convenient, you can model bind directly to collections of primitive or complex types. Here's 2 examples:

index.cshtml:

@using (Html.BeginForm("ListStrings", "Home"))
{
    <p>Bind a collection of strings:</p>

    <input type="text" name="[0]" value="The quick" /><br />
    <input type="text" name="[1]" value="brown fox" /><br />
    <input type="text" name="[2]" value="jumped over" /><br />
    <input type="text" name="[3]" value="the donkey" /><br />

    <input type="submit" value="List" />
}

@using (Html.BeginForm("ListComplexModel", "Home"))
{
    <p>Bind a collection of complex models:</p>

    <input type="text" name="[0].Id" value="1" /><br />
    <input type="text" name="[0].Name" value="Bob" /><br />
    <input type="text" name="[1].Id" value="2" /><br />
    <input type="text" name="[1].Name" value="Jane" /><br />

    <input type="submit" value="List" />
}

Student.cs:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
}

HomeController.cs:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult ListStrings(List<string> items)
    {
        return View(items);
    }

    public ActionResult ListComplexModel(List<Student> items)
    {
        return View(items);
    }
}

ListStrings.cshtml:

@foreach (var item in Model)
{
    <p>@item</p>
}

ListComplexModel.cshtml:

@foreach (var item in Model)
{
    <p>@item.Id. @item.Name</p>
}

The first form simply binds a list of strings. The second, binds the form data to a List<Student>. By using this approach, you can let the default model binder do some of the tedious work for you.

Updated for comment

Yes you can do that too:

Form:

@using (Html.BeginForm("ListComplexModel", "Home"))
{
    <p>Bind a collection of complex models:</p>

    <input type="text" name="[0].Id" value="1" /><br />
    <input type="text" name="[0].Name" value="Bob" /><br />
    <input type="text" name="[1].Id" value="2" /><br />
    <input type="text" name="[1].Name" value="Jane" /><br />
    <input type="text" name="ClassId" value="13" /><br />

    <input type="submit" value="List" />
}

Controller action:

public ActionResult ListComplexModel(List<Student> items, int ClassId)
{
    // do stuff
}
John H
  • 14,422
  • 4
  • 41
  • 74
  • Can I combine this with an additional argument that I call "int ClassId", which comes from the form too? – Mathias Lykkegaard Lorenzen Feb 14 '12 at 18:01
  • 1
    @MathiasLykkegaardLorenzen Yep, updated answer to show an example. As an aside, if you want to know how to use display templates with that data, Phil Haack covers the whole thing [here](http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx). – John H Feb 14 '12 at 18:06
  • @MathiasLykkegaardLorenzen Can you give me an example of what you mean? – John H Feb 14 '12 at 19:33
  • Nevermind, I got that part fixed. Now I'm facing a different issue. My Student has a "point" value as a double. However, when I launch my form, it doesn't work, because of the server's localization settings. It expects a "," decimal separator instead of a "." decimal separator. What do I do here? – Mathias Lykkegaard Lorenzen Feb 14 '12 at 20:00
  • 1
    @MathiasLykkegaardLorenzen This is a separate but common problem. Check Phil Haack's [post](http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx) on the subject and also [this question](http://stackoverflow.com/questions/5050641/asp-net-mvc-model-binder-with-global-number-formats) for another perspective. – John H Feb 14 '12 at 20:18
3

Mathias,

This works perfectly well without recourse to the params object. your form controls:

<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="professorName" />

You would use the FormCollection object, which will contain all your form elements as either comma separated lists (if a control array) or as single properties. In the above example, this is what we'd get:

[HttpPost]
public ActionResult PostStudentNames(FormCollection formValues)
{
    // basic check for rogue commas inside input controls
    // would need far more sophistication in a #real# app :)
    var valueStudents = formValues["studentName"].Split(',')
                          .Where(x => x.Length > 0).ToArray();
    var valueProfessor = formValues["professorName"];
    // other stuff
}

etc... At least, this is my recollection of this from a recent project. :)

jim tollan
  • 22,305
  • 4
  • 49
  • 63
  • yeah - i almost 'gag' anytime i have to open up a webforms project for maintenance these days. mvc is just so 'right' (for me anyway - i know it's a matter of taste and style) – jim tollan Feb 14 '12 at 17:51
  • if studentName input have ',' in value what happens – Yorgo Feb 14 '12 at 17:52
  • Yorga - yes, in the primitive example that i made, that could happen. a check for dodgy commas would of course be a prescient objective. good catch - could use `var valueStudents = formValues["studentName"].Split(',').Where(x => x.Length>0).ToArray();` of course. will update answer – jim tollan Feb 14 '12 at 17:54
1
<input type="text" name="studentName[0]"></input>
<input type="text" name="studentName[1]"></input>
<input type="text" name="studentName[2]"></input>

public ActionResult PostStudentNames(string[] studentName)
{
}
Yorgo
  • 2,668
  • 1
  • 16
  • 24