I'm working at an ASP.NET MVC website using C#.
On a page I try to send back a complete model to the controller. In this model most inputs are unique variables, and there is an array.
In the model, the array is defined like this:
public class SimulazioneModelComplete
...
public SoaValueModel[] GareSoaSec { get; set; }
The razor / html code (very simplified!) is like this
#model SimulazioneModelComplete
#using (Html.BeginForm("CreaEsito", "Simulazioni", FormMethod.Post, new { id = "CreaEsito", name = "CreaEsito", enctype = "multipart/form-data" }))
{
...
#foreach (SoaModel soa in ViewBag.Categorie)
<input type="checkbox" name="GareSoaSec[#counter].IdSoa" id="GareSoaSec[#counter].IdSoa" value="#soa.id" />
...
}
The user posts data clicking a button:
<button onclick="Save()">SAVE</button>
The onclick javascript is like this:
function Save()
{
$.post("/Abbonamenti/Simulazioni/CreaEsito", $("CreaEsito").serialize(), function (data) { });
};
The CreaEsito method is like this:
[HttpPost]
[AuthorizedOnly(Roles = "Administrator, Agent, Esiti")]
public ActionResult CreaEsito(SimulazioneModelComplete model, SoaValueModel[] GareSoaSec)
I try in a lot of ways, but I never have a complete model, with both the simple variables and the array of objects. It seems like the "link" made by "name" doesn't work properly, and I don't understand why.
It should work well (I do the same in other pages, and everything works well), but this time it doesn't work. When I look the model sent back to the controller, the unique variables are ok, the array is empty, length is zero.
I've lost all Friday and Saturday, now is 10PM and I'm still fighting, without any solution.
Can someone help?
Related
I work on ASP.NET MVC application. Table is being populated using jQuery, but it doesn't matter now.
The problem is this:
When I make a call with form tags, I get an error 404 Not Found. By inspecting I confirmed that model is bound OK (it's populated).
When I comment out form tags, I can make a call to method, but with model being null (like it's not bound).
Here is some of code I use:
#model PayrollModel
#*#using (Html.BeginForm("Index", "Request", FormMethod.Post, new { id = "frmPersonView" }))
{*#
<table data-dataurl="#Url.Action("PayrollData", "Payroll")" id="tblData" class="table hover table-bordered table-striped"></table>
#*}*#
Here is my controller method:
[HttpGet]
public ActionResult PayrollData(PayrollModel model)
{
//do something
}
Edited:
I found out why this happens:
Request URL:
http:// localhost/CT.ISVDV.WebUI/Payroll/PayrollData?sEcho=1&iColumns=5&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=FIRSTNAME&mDataProp_1=LASTNAME&mDataProp_2=RIGHTGROUPNAME&mDataProp_3=DISABILITYTYPENAME&mDataProp_4=AMOUNT&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&bSortable_1=true&bSortable_2=true&bSortable_3=true&bSortable_4=true&SelectedTypes%5B3%5D=4&SelectedTypes%5B4%5D=5&SelectedTypes%5B5%5D=6&SelectedTypes%5B6%5D=7&SelectedTypes%5B7%5D=8&SelectedTypes%5B8%5D=9&SelectedTypes%5B9%5D=10&SelectedTypes%5B10%5D=11&SelectedTypes%5B11%5D=12&SelectedTypes%5B12%5D=13&SelectedTypes%5B13%5D=14&SelectedTypes%5B14%5D=15&SelectedTypes%5B15%5D=16&SelectedTypes%5B16%5D=17&SelectedTypes%5B17%5D=18&SelectedTypes%5B18%5D=19&SelectedTypes%5B19%5D=20&SelectedTypes%5B20%5D=21&SelectedTypes%5B21%5D=22&SelectedTypes%5B22%5D=23&SelectedTypes%5B23%5D=24&SelectedTypes%5B24%5D=25&SelectedTypes%5B25%5D=26&PayrollDate=01.12.2014&PayrollSearch.RIGHTGROUPIDList.Value%5B0%5D=4&PayrollSearch.RIGHTGROUPIDList.Value%5B1%5D=5&PayrollSearch.RIGHTGROUPIDList.Value%5B2%5D=6&PayrollSearch.RIGHTGROUPIDList.Value%5B3%5D=7&PayrollSearch.RIGHTGROUPIDList.Value%5B4%5D=8&PayrollSearch.RIGHTGROUPIDList.Value%5B5%5D=9&PayrollSearch.RIGHTGROUPIDList.Value%5B6%5D=10&PayrollSearch.RIGHTGROUPIDList.Value%5B7%5D=11&PayrollSearch.RIGHTGROUPIDList.Value%5B8%5D=12&PayrollSearch.RIGHTGROUPIDList.Value%5B9%5D=13&PayrollSearch.RIGHTGROUPIDList.Value%5B10%5D=14&PayrollSearch.RIGHTGROUPIDList.Value%5B11%5D=15&PayrollSearch.RIGHTGROUPIDList.Value%5B12%5D=16&PayrollSearch.RIGHTGROUPIDList.Value%5B13%5D=17&PayrollSearch.RIGHTGROUPIDList.Value%5B14%5D=18&PayrollSearch.RIGHTGROUPIDList.Value%5B15%5D=19&PayrollSearch.RIGHTGROUPIDList.Value%5B16%5D=20&PayrollSearch.RIGHTGROUPIDList.Value%5B17%5D=21&PayrollSearch.RIGHTGROUPIDList.Value%5B18%5D=22&PayrollSearch.RIGHTGROUPIDList.Value%5B19%5D=23&PayrollSearch.RIGHTGROUPIDList.Value%5B20%5D=24&PayrollSearch.RIGHTGROUPIDList.Value%5B21%5D=25&PayrollSearch.RIGHTGROUPIDList.Value%5B22%5D=26&tblData_length=10&
Up to this point, everything is right, but then i also passes some strange values that are not model properties. This happens only if I use form tags, and I need those.
DXScript=1_157%2C1_89%2C1_88%2C17_28%2C17_2%2C1_149%2C1_86%2C1_141%2C1_96%2C17_7%2C1_139%2C1_98%2C1_97%2C17_8%2C1_155%2C1_125%2C1_156%2C1_118%2C17_9%2C1_148%2C1_147%2C1_132%2C17_27%2C1_142%2C1_93%2C1_119%2C1_99%2C1_151%2C1_126%2C17_13%2C1_108%2C1_115%2C1_137%2C1_92%2C1_152%2C1_128%2C17_15%2C1_129%2C1_120%2C17_11%2C1_131%2C1_140%2C1_134%2C17_18%2C1_145%2C17_20%2C1_143%2C1_138%2C1_146%2C1_150%2C17_23%2C17_26%2C1_95%2C5_5%2C5_4%2C4_11%2C4_10%2C4_6%2C4_7%2C4_9%2C17_14%2C4_12%2C1_107%2C1_110%2C4_13%2C4_14%2C1_106%2C1_124%2C17_12%2C1_144%2C7_48%2C1_91%2C7_50%2C17_19%2C1_100%2C1_103%2C1_111%2C17_0%2C1_114%2C1_101%2C17_1%2C1_102%2C17_3%2C1_104%2C1_116%2C17_5%2C1_130%2C1_113%2C17_16%2C17_17%2C1_112%2C17_24%2C1_117%2C10_2%2C10_1%2C10_3%2C10_4%2C17_4%2C9_15%2C9_12%2C9_10%2C17_21%2C9_14%2C9_11%2C9_13%2C8_10%2C8_17%2C8_24%2C8_26%2C8_9%2C8_12%2C8_13%2C8_18%2C17_22%2C8_21%2C8_23%2C8_22%2C8_16%2C8_19%2C8_20%2C8_14%2C8_15%2C8_25%2C8_11%2C6_12%2C17_25&DXCss=%2FCT.ISVDV.WebUI%2Fimg%2Ffavicon.ico%2C%2FCT.ISVDV.WebUI%2FContent%2Fjvectormap%2Fjquery-jvectormap-1.2.2.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fbootstrap.min.css%2C%2FCT.ISVDV.WebUI%2FContent%2Ffont-awesome.min.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fionicons.min.css%2C%2FCT.ISVDV.WebUI%2FContent%2FiCheck%2Fall.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fcolorpicker%2Fbootstrap-colorpicker.min.css%2C%2FCT.ISVDV.WebUI%2FContent%2Ftimepicker%2Fbootstrap-timepicker.min.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fmorris%2Fmorris.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fdatepicker%2Fdatepicker3.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fdaterangepicker%2Fdaterangepicker-bs3.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fbootstrap-wysihtml5%2Fbootstrap3-wysihtml5.min.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fcss%2Fselect2.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fselect2-bootstrap.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fdatatables%2FdataTables.bootstrap.css%2C%2FCT.ISVDV.WebUI%2FContent%2FAdminLTE.css%2C%2FCT.ISVDV.WebUI%2FContent%2FSite.css%2C%2FCT.ISVDV.WebUI%2FContent%2Fjquery.bootstrap-touchspin%2Fjquery.bootstrap-touchspin.css%2C1_9%2C1_11%2C0_844%2C0_842%2C1_8%2C0_691%2C0_694%2C0_682%2C1_4%2C0_684%2C0_805%2C0_698%2C4_2%2C0_700%2C5_1%2C0_782%2C0_762%2C0_764%2C7_1%2C7_0%2C1_1%2C0_657%2C0_869%2C0_871%2C0_777%2C8_2%2C0_779%2C8_0%2C0_794%2C6_2%2C0_796&_=1417596051723
How can I prevent this from happening?
Your formatting is off. Try mix single quotes with double ones:
data-dataurl='#Url.Action("PayrollData", "Payroll")'
PayrollData expects a PayrollModel model and you're providing none. There's no reasonable way to send a whole model using Url.Action so consider sending only an ID.
data-dataurl='#Url.Action("PayrollData", "Payroll", new { id = model.ID })'
I think you are very confused on how it all works, especially model binding.
Normally you would have something like this (Lets say we are working only have Payroll controller)
[HttpGet]
public ActionResult Index()
{
// here you would get your model that displays some data on that page
PayrollIndexModel model = fetchModelData();
return View(model)
}
and its view
#model PayrollIndexModel
#Model.PageTitle
#using (Html.BeginForm("Request", "Payroll", FormMethod.Post, new { id = "frmPersonView" }))
{
<table data-dataurl="#Url.Action("PayrollData", "Payroll")" id="tblData" class="table hover table-bordered table-striped"></table>
}
then if you click Save on the form you would call the one below where PayrollModel is binded to your model class and later you can do things with it.
[HttpPost]
public ActionResult Request(PayrollModel model)
{
// save stuff to database
}
To get this one working
#Url.Action("PayrollData", "Payroll")
You have to create actions this way as you are requesting data by GET method and not sending anything to the server:
[HttpGet]
public ActionResult PayrollData()
{
PayrollModel model = fetchModelData();
return View(model)
}
However, I see that you use DataTables library and last time I used it I remember that Datatable is expecting JSON object. So here you should send JSON.
[HttpGet]
public ActionResult PayrollData()
{
PayrollModel model = fetchModelData();
return Json(model)
}
Your model converted to JSON should be formatted in the way that JQuery DataTable can parse it (understand it) such as:
{ "iTotalRecords":11,
"iTotalDisplayRecords":11,
"sEcho":1,
"aaData":[{"FirstName":"Dan","LastName":"Callahan","PhoneNumber":"(123) 555-5552","Age":35,"Birthday":"\/Date(218955600000)\/"},
{"FirstName":"Tom","LastName":"Gun","PhoneNumber":"(123) 555-5559","Age":59,"Birthday":"\/Date(-534538800000)\/"},
{"FirstName":"James","LastName":"Halk","PhoneNumber":"(123) 555-5554","Age":21,"Birthday":"\/Date(660027600000)\/"},
{"FirstName":"Jarold","LastName":"Interface","PhoneNumber":"(123) 555-5556","Age":39,"Birthday":"\/Date(86932800000)\/"},
{"FirstName":"Kevin","LastName":"Kentucky","PhoneNumber":"(123) 555-5551","Age":40,"Birthday":"\/Date(92638800000)\/"},
{"FirstName":"Justin","LastName":"Michaels","PhoneNumber":"(123) 555-5555","Age":27,"Birthday":"\/Date(470898000000)\/"},
{"FirstName":"Erich","LastName":"Milton","PhoneNumber":"(123) 555-5558","Age":54,"Birthday":"\/Date(-370724400000)\/"},
{"FirstName":"Mike","LastName":"Peterson","PhoneNumber":"(123) 555-5550","Age":24,"Birthday":"\/Date(568184400000)\/"},
{"FirstName":"Jason","LastName":"Ralph","PhoneNumber":"(123) 555-5557","Age":27,"Birthday":"\/Date(468306000000)\/"},
{"FirstName":"John","LastName":"Thompson21","PhoneNumber":"(123) 555-5545","Age":27,"Birthday":"\/Date(473317200000)\/"}
]}
To make your life easier check this library which does a lot of tedious work for you:
https://github.com/ALMMa/datatables.mvc
Good Luck.
I have this model that has a list of another model. Then in my view, I have my form that fills a couple of fields for my main Model. But I want this form to also be able to add X models of the other type and to be all wired up and I'm wondering how to properly do this.
So here are my two models:
public class MyMainModel
{
public int MyMainId { get; set; }
[Required(ErrorMessage = "Groovy name required")]
[Display(Name = "MyMainModel's groovy name:")]
public string Name { get; set; }
public List<MySubModel> MySubModels { get; set; }
}
public class MySubModel
{
public int MySubId { get; set; }
[Required(ErrorMessage = "Cool name required")]
[Display(Name = "MySubModel's cool name:")]
public string Name { get; set; }
}
When I hit my controller for my "create" view, I go through this action:
public ActionResult SomePageAboutCreating()
{
// [...] Some other stuff
return View(new MyMainModel());
}
Now this sends to my strongly typed view:
#model myFunProject.WebModels.MyMainModel
<div>
<form id="my-create-form" onsubmit="CreateMyMainModel(this); return false;">
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="result" style="background-color: silver;">This is the operation result box</div>
<img class="loading" src="/Images/ajax-loader.gif" alt="Loading..." width="16" height="16" style="display: none;" />
<section>
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
</section>
<!-- Here begins the fields for my list of "MySubModel" -->
#Html.EditorFor(x => x.MySubModels)
<!-- Here I'm thinking about a javascript that will add the previous fields X times so that the user can create "MyMainModel" as well as x times "MySubModel"... -->
<input type="submit" class="btn-send" id="my-create-form-submit" value="Send" />
</form>
</div>
So I think I have to use the EditorTemplates here... So I setup in my /Views/EditorTemplates/MySubModels.cshtml (Named against my "MyMainModel"'s property) and then when I write my form in there, I'm confused...
#model myFunProject.WebModels.MyMainModel
#*<section>
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
</section>*#
So here I'm not sure what to put in here... I want my Name property there to be the one of "MySubModel". And as the user sees this form, say for example he'll go through this scenario:
Enters a name for the "MyMainModel".
Goes to the other name box and fills in a name of the first instance of "MySubModel.
Then he would click a special button that would manipulate dom to append another MySubModel.Name field.
He would write in the second "MySubModel" name.
He would click submit.
The ajax call I put in there I'm ok doing the wiring up, but my confusion comes with the code I have to write for the editor template and then I'm kind of also wondering about how I'm going to create a new field (for that second "MySubModel" for example...).
Any help would be appreciated, I've gone through many articles about subjects close to this, but have not found this case yet. Thanks!
EDIT:
I'll add the action (an overly simplified version hehe) that is called by my ajax when the form is submitted.
public ActionResult CreateMyMainModel(MyMainModel myMainModel) {
// [...] Do stuff like save to database...
// var aGroovyNAme = myMainModel.Name;
foreach(var mySubModel in myMainModel.MySubModels) {
// Here I would have the sub models available to manipulate...
// var aCoolName = mySubModel.Name;
}
return Content("ok");
}
I've gone through many articles about subjects close to this, but have not found this case yet.
I would really recommend you reading the editing a variable length list article from Steven Sanderson which illustrates a very nice approach to handle this scenario. He presents a custom Html.BeginCollectionItem helper which could be used to generate non-sequential indexes (guids) for the input field names and thus allowing for easily removing elements dynamically without leaving holes in the indexes. When the user decides to add another item, an AJAX call is made to a controller action which simply returns an empty template (partial).
You could also do this purely on the client side with javascript only. Steven Sanderson illustrated this approach using knockoutjs in this similar article.
Those two articles are really the best approach in terms of dynamically editing a variable length list of items in ASP.NET MVC. Reading them will really be helpful for better understanding some core concepts in model binding in ASP.NET MVC.
I had a similar issue on a project where I wanted to validate some fields on some occasions
and not on others (ie on save don't validate but on submit validate everything.
I ended up doing everything manually in javascript and posting back a json object.
On reflection I would rather have manipulated the validation javascript file(MicrosoftMVCValidation.js).
For model binding issues, I recommended looking at custom model binding.
I found using EditorTemplates a bit fiddly especially with partial views.
I find asp.net mvc 3 a bit weak in model binding. I hoped that some of the issues would be fixed in mvc 4 but from what I have looked at so far MVC4 is primarily an upgrade for creating windows phone applications.
Here's an example of a custom model binder for decimal properties in the model.
You can use the same logic with your own custom models.
I found there are occasions when I want to bind a model with a collection of entities on large pages, rather than just binding a basic collection of properties for example.
public class DecimalModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
//return valueProviderResult == null ? base.BindModel(controllerContext, bindingContext) : Convert.ToDecimal(valueProviderResult.AttemptedValue);
if (valueProviderResult == null)
return base.BindModel(controllerContext, bindingContext);
else if (valueProviderResult.AttemptedValue == "")
return base.BindModel(controllerContext, bindingContext);
else
return Convert.ToDecimal(valueProviderResult.AttemptedValue);
}
}
I've recently answered pretty much this exact question on another thread:
ASP.Net MVC4 bind a "create view" to a model that contains List
I have a feeling this could be a basic question!
I have a complex object ie a document object which contains properties that are lists of other objects. It has been created by deserializing some XML.
I would like to pass the entire Model to the View
Return ViewResult(MyDoc)
In the View some properties are edited. Control is then returned back to the Post Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(Document myDoc)
"myDoc" now just represents my Form's fields. I suspect this is ModelBinding at work. So I guess I need to persist my complex object in a hidden field(example would be great) or as a session object. However I am a bit confused how my updated field gets merged back into the persisted object(hidden or session).
Is storing that large object in the view entirely necessary?
The View seems to have no control over that content, so you probably don't want to send all that data to the View. It seems you are applying a mind-set based on ViewState, which doesn't really fit very well with MVC, IMHO.
Communication between the View and the Controller is done through ViewModels, and there's usually no need to store any large serialized data and making that go back and forth when interacting with the server.
Can't you create a ViewModel that represents only the useful data for the view (the fields), and get that data back in your Action by receiving the ViewModel during POST, and afterwards synchronize the information you obtained from the View with what you load from your XML only at that time?
You are right, this is model binding at work.
The binding occurs almost automatically when you use HtmlHelpers such as :
#Html.TextboxFor(model => model.PropertyName)
This line actually creates something a little bit like this :
<input type="textbox" id="Modelobject_PropertyName" name="ModelObject.PropertyName" />
Then when you submit your form, the DefaultModelBinder is able to deserialize the POST value and create and object of the given type (at least it will try), if it can't find a corresponding entry, the property will be null and if an entry have no corresponding property, it will be ignored (unless you have other parameters).
You can read this article it's a little bit old, but it's still pretty accurate.
As an exemple:
Let's say you have a simple object :
public class IndexModel
{
public string MyProperty { get; set; }
public bool MyCheckbox { get; set; }
}
A simple controler :
public class TestingController : Controller
{
[OutputCache(Duration=0, NoStore = true)]
public ActionResult Index()
{
return View(new IndexModel { MyProperty = "hi" });
}
[HttpPost]
[OutputCache(Duration=0, NoStore = true)]
public ActionResult Index(IndexModel model)
{
model.MyProperty += "!";
ModelState.Clear();
return View(model);
}
}
And a simple view :
#model MvcApp.Models.IndexModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(model => model.MyProperty)<p />
#Html.TextBoxFor(model => model.MyProperty)
</div>
<div>
#Html.LabelFor(model => model.MyCheckbox)<p />
#Html.CheckBoxFor(model => model.MyCheckbox)
</div>
<input type="submit" />
}
You will see, when you submit the form, that the model is recreated completely.
If you don't want to display the actual value of a property, but still need it persisted:
#Html.HiddenFor(model => model.MyProperty)
This will generate a hidden field that will be posted back and, therefor, persisted.
Happy coding !
I have been trying to do the following but whatever I try I just keep getting different errors:
#using (Html.BeginForm(Url.Action(this.ViewContext.RouteData.Values["action"] as string)))
This for example produces:
<form action="/adminTests/create?Length=22" method="post">
Has anyone figured out how to do this?
You are using a wrong overload. It should be:
#using (Html.BeginForm(new { action = ViewContext.RouteData.Values.GetRequiredString("action") }))
or:
#using (Html.BeginForm(ViewContext.RouteData.Values.GetRequiredString("action"), ViewContext.RouteData.Values.GetRequiredString("controller"))))
or if you want to generate a form POSTing to the current url use simply (note that this will include any query string parameters):
#using (Html.BeginForm())
For full list of available overloads consult the documentation.
Perhaps try this:
#using (Html.BeginForm("action", "controller"))
This uses the correct overload and has a simpler (imo) syntax.
Edge case answer
If you are posting back to an action of the same name, but you loaded the form with more data from the route you have to do one more thing. In your controller, you need to remove the extra routing values.
RouteData.Values.Remove("CompanyId");
Let me explain.
In my case I was loading the Add Contact form passing it the company id in the route, e.g., contact/add/123 to the Add action public IActionResult Add(int companyId). The Add/Edit contact form is then loaded with a new ViewModel with the CompanyId populated. var model = new ContactViewModel { CompanyId = companyId };
The route now serves no purpose, but if you try to use Html.BeginForm("add", "contact") the form will still have an action of contact/add/123, which will cause the routing to fail.
You can work around this by taking the id on the add and ignoring it (yuk!). Or by passing parameters rather than using routing (probably best), but sometimes there are external reasons that's not an option and you have to work with the situation you have.
The Action in one go for easier reading
[HttpGet]
[Route("add/{companyId}")]
public IActionResult Add(int companyId)
{
var model = new ContactViewModel { CompanyId = companyId };
RouteData.Values.Remove("companyId");
return PartialView("_ContactModal", model);
}
[HttpPost]
[Route("add")]
public IActionResult Add(ContactViewModel model)
{
...
}
My application writes custom attributes to input controls using Helper classes. And also we are loading the UserControl dynamically so we need to use FormCollection to get the posted values.
Is there a way that we can access attribute values from FormCollection object.
Example:
<input type="text" name="textBox1" value="harsha" customAttr1 = "MyValue" />
My question is how can i access the value of customAttr1 from the above eg from inside the controller.
Thanks for the help in advance..
How is your helper structured? If it's extending HtmlHelper, you can access ViewContext.HttpContext.Request.Form, which is a NameValueCollection; FormCollection is used by the model binder to post values back to an action method. Its not publicly exposed anywhere else.
HTH.
Simple answer is no am afraid, the formCollection only contains the basic Key and Value information.
It might be easier to rehydrate that information once you are in the controller? using some sort of mechanic to identify what you passed in.
An alternative is if you have a list of controls that map to a base type then you could loop through each control.
MVC is a bit magic and can map properties back to a model, even to a list.
If you have a model which has a list of Controls:
public class Control
{
String Value {get; set;}
String Attribute1 {get; set;}
}
public class ControlViewModel
{
IList<Control> Controls {get; set;}
}
then in your view:
for(var i = 0; i<controls.Count;i++)
{
// Obviously this isnt complete right i needs to increment from 0; would be build using your htmlhelpers.
<input id="Controls[i]_Value" name="Controls[i].Value" type="text" value="hello" />
<input id="Controls[i]_Attribute1" name="Controls[i].Attribute1" type="hidden" value="Attribute" />
}
in your httppost action you can then collect the ControlViewModel and the Controls list should populate.
I havent tested this, there are probably plenty of errors, but this should be enough to get started; posts out there that discuss this, if I find any after posting I'll add them.
As Luke already told.. Form Collection is dictionary object and only holds name,value pair.. in order to get that thing into controller you need to pass that custom attribute through ajax.
var form = $("#formid").serialize(),
custom = $("input:text").attr("customAttr1").val();
$.ajax({
type: "POST",
url: "/controller/ProcessData",
data:{collection :form,customAttr: custom },
dataType: "html",
traditional: true
});
in the controller you need to have following syntax:
public ActionResult ProcessData(FormCollection collection ,string customAttr)
{
in case you need to pass multiple custom values you need to post string array from ajax request and make controller signature like:
public ActionResult ProcessData(FormCollection collection ,string[] customArray)
{