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?
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
}
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. :)
<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)
{
}
Related
My Controller
public class OgrenciController : Controller
{
[HttpPost]
public ActionResult home(int perId)
{
if(perId=="anything")
{
//something
}
return View();
}
}
This is my view
#foreach (var item in Model)
{
siraNo++;
<form method="post">
<input type="radio" name="perId" value="#item.specialId">
<input type="submit" value="send" />
</form>
}
but in the POST method, perId not take anythings
use something like this.
#using (Html.BeginForm())
{
foreach (var item in Model)
{
<input type="radio" name="perId" value="#item.specialId"><br /><br />
}
<input type="submit" ur value="send" />
}
View
#if (weekMaster != null)
{
using (Html.BeginForm("UpdatePlan", "generalPlan", FormMethod.Post, new { }))
{
<table class="table-bordered">
<tr>
#foreach (TermMaster obj in weekMaster.ToList())
{
<td align="center">
<span> #obj.termStartDate.ToString("dd MMM") - #obj.termEndDate.ToString("dd MMM")</span>
<br />
<input type="hidden" name="ObjHid" value="#obj" />
<input type="hidden" name="startDate" value="#obj.termStartDate" />
<input type="hidden" name="endDate" value="#obj.termEndDate" />
<input type="text" style="width:80%" name="weekSession" />
</td>
}
<td>
<input type="submit" value="Update" class="btn-primary" />
</td>
</tr>
</table>
} }
Controller
[HttpPost]
public ActionResult UpdatePlan(List<DateTime> startDate, List<DateTime> endDate, List<int> weekSession, List<TermMaster> ObjHid)
{
return View();
}
I am trying pass Class Object from View to Controller above TermMaster class Object pass using input method <input type="hidden" name="ObjHid" value="#obj" /> but showing NULL value if pass single value like startDate and endDate then it work fine.
What is wrong in my code? how to pass class object List in Post Method?
Please refer Image
You can not bind objects to controller from your input. You can serialize object to json. In the controller you can take your inputs value as string and deserialize it.
You have to do it by below approach.
Create a model instead of multiple parameters, and use index in cshtml.
public class model
{
public List<DateTime> startDate { get; set; }
public List<DateTime> endDate { get; set; }
public List<int> weekSession { get; set; }
public List<TermMaster> ObjHid { get; set; }
}
CSHTML
#{ int i = 0; }
#foreach (TermMaster obj in weekMaster.ToList())
{
<td align="center">
<span> #obj.termStartDate.ToString("dd MMM") - #obj.termEndDate.ToString("dd MMM")</span>
<br />
<input type="hidden" name="ObjHid[#i].termStartDate" value="#obj.termStartDate.ToString("dd MMM")" />
<input type="hidden" name="ObjHid[#i].termStartDate" value="#obj.termStartDate.ToString("dd MMM")" />
<input type="hidden" name="startDate[#i]" value="#obj.termStartDate" />
<input type="hidden" name="endDate[#i]" value="#obj.termEndDate" />
<input type="text" style="width:80%" name="weekSession[#i]" />
</td>
i++
}
I'm learning ASP.NET MVC and having a problem with saving a model as below:
Orders -> OrderItems
In my Create view , I will add some items into Order and when I press submit, I want the items data bind to model.OrderItems. How can I achieve this ?
[HttpPost]
public ActionResult Create(OrderViewModel model)
{
...............
//iterate item list and add to main order
foreach (var item in model.OrderItems) <~ this is what I'm trying to do
{
}
...............
}
Thanks a lot.
If they're sequential, you can create an index on each of your inputs:
<input type="text" name="OrderItems[0].Name" value="Stuff" />
<input type="text" name="OrderItems[0].Price" value="5.00" />
...
<input type="text" name="OrderItems[1].Name" value="OtherStuff" />
<input type="text" name="OrderItems[1].Price" value="15.00" />
Or you'll need to provide an index:
<input type="hidden" name="OrderItems.Index" value="#item.ItemId" />
<input type="text" name="OrderItems[#item.ItemId].Name" value="Stuff" />
<input type="text" name="OrderItems[#item.ItemId].Price" value="5.00" />
My problem here is how to send the list values to the controller upon an ajax post. With my actual code, the list is null on post.
My view:
#using (Html.BeginForm("UsersList", "Project", FormMethod.Post, new { id = "Users" }))
{
<div id="MyList" >
<table>
<thead>
<tr><th></th><th>User</th><th>End date</th></tr>
</thead>
#Html.EditorFor(x => x.GetUsers)
</table>
</div>
<script type="text/javascript">
$(function () {
$('#MyList).dialog({
autoOpen: false,
width: 820,
buttons: {
"Save": function () {
$("#Users").submit();
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
</script>
Model:
public class ProjectModel
{
public List<ProjectList>GetUsers{get;set;}
}
public class ProjectList
{
public bool Selected { get; set; }
public int UserID { get; set; }
}
My controller:
[HttpPost]
public ActionResult UsersList(ProjectModel model)
{
return View(model);
}
Sending a list can be difficult. I think MVC 3.0 has (in-built) support for sending JSON to the controller which would make this easier.
However with prior versions I think this article should be what your looking for:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Effectively you give everything in the list the same name in your HTML (as the name will be what gets sent in the key/value string that gets sent and it will bind as a list.
For more complex items you name each object property but add an index to show which object they get attached too:
<form method="post" action="/Home/Create">
<input type="text" name="[0].Title" value="Curious George" />
<input type="text" name="[0].Author" value="H.A. Rey" />
<input type="text" name="[0].DatePublished" value="2/23/1973" />
<input type="text" name="[1].Title" value="Code Complete" />
<input type="text" name="[1].Author" value="Steve McConnell" />
<input type="text" name="[1].DatePublished" value="6/9/2004" />
<input type="text" name="[2].Title" value="The Two Towers" />
<input type="text" name="[2].Author" value="JRR Tolkien" />
<input type="text" name="[2].DatePublished" value="6/1/2005" />
<input type="submit" />
Has anyone had any luck model binding two or more collections using the code posted by Phil Haack here: Model Binding To A List?
As an example, I have the below code.
public class Book {
public string Name { get; set; }
}
public class Author {
public string Name { get; set; }
}
public ActionResult Index(List<Book> books, List<Author> authors) {
// Will never model bind two collections.
}
The HTML that I have is:
<input type="hidden" name="books.index" value="1" />
<input type="text" name="books[1].Name" />
<input type="hidden" name="books.index" value="2" />
<input type="text" name="books[2].Name" />
<input type="hidden" name="authors.index" value="1" />
<input type="text" name="authors[1].Name" />
<input type="hidden" name="authors.index" value="1" />
<input type="text" name="authors[1].Name" />
The exception that I get is:
The parameters dictionary contains an invalid entry for parameter 'authors' for method 'System.Web.Mvc.ActionResult Index(System.Collections.Generic.List1[Book], System.Collections.Generic.List1[Author])' in 'HomeController'. The dictionary contains a value of type 'System.Collections.Generic.List1[Book]', but the parameter requires a value of type 'System.Collections.Generic.List1[Author]'. Parameter name: parameters
Am I doing something wrong or is this not supported by ASP.NET MVC?
Your problem is somewhere else, I was unable to reproduce. The following works fine for me:
Model:
public class Book
{
public string Name { get; set; }
}
public class Author
{
public string Name { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(List<Book> books, List<Author> authors)
{
return View();
}
}
View:
<% using (Html.BeginForm()) { %>
<input type="text" name="books[0].Name" value="book 1" />
<input type="text" name="books[1].Name" value="book 2" />
<input type="text" name="authors[0].Name" value="author 1" />
<input type="text" name="authors[1].Name" value="author 2" />
<input type="submit" value="OK" />
<% } %>
It successfully binds values back in the POST action.
UPDATE:
I confirm that this is a bug in ASP.NET MVC 3 RC2 which will be fixed in the RTM. As a workaround you could put the following in your Application_Start:
ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider();