after generating a question list to let user key in, i will like to post their input and save into database, but my view model can't get what user key in in the textfield. Any hint or guidance?
My viewmodel:
#model List<SurveyTool.Models.AnswerQuestionViewModel>
#{
ViewBag.Title = "Edit";
}
<br/>
<h2>Questions</h2>
<br/>
#using (Html.BeginForm())
{
<table>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => m[i].Survey_ID)
#Html.HiddenFor(m => m[i].MaxChar)
#Html.DisplayFor(m => m[i].Question)
</td>
</tr>
<tr>
#switch (Model[i].Type)
{
case "Info_Text":
{
<td></td>
}
break;
case "Single_Line":
{
<td>
#Html.EditorFor(m=>m[i].Answer, new { #maxlength = Model[i].MaxChar}) << textbox to input answer.
</td>
}
break;
case "Multiple_Line":
{
<td>
#Html.TextAreaFor(m => m[i].Answer)
</td>
}
break;
}
</tr>
}
</table>
<input value="Submit" type="submit" class="btn" />
}
My Controller:
[HttpPost]
public ActionResult SURV_Answer_Submit(AnswerQuestionViewModel viewmodel)
{
if (ModelState.IsValid)
{
var query = from r in db.SURV_Question_Ext_Model.ToList()
join s in db.SURV_Question_Model
on r.Qext_Question_ID equals
s.Question_ID
where s.Question_Survey_ID == viewmodel.Survey_ID && r.Qext_Language == viewmodel.Language
orderby s.Question_Position ascending
select new { r, s };
foreach(var item in query)
{
var answer = new SURV_Answer_Model();
answer.Answer_Qext_ID = item.r.Qext_Question_ID;
answer.Answer_Data = viewmodel.Answer; << can't get input!
db.SURV_Answer_Model.Add(answer);
db.SaveChanges();
}
return RedirectToAction("SURV_Main_Index", "SURV_Main");
}
return View(viewmodel);
}
You model in the view is defined as #model List<AnswerQuestionViewModel> which means the POST method signature need to be changed to
public ActionResult SURV_Answer_Submit(List<AnswerQuestionViewModel> viewmodel)
The use a loop to access each item in the model and save.
However there are also some other issues. The DefaultModelBinder required collection indexers to start at zero and be consecutive, but your #switch (Model[i].Type) statement means that a form control for Answer may not be rendered. To ensure binding does not fail when you submit, include a hidden input for case "Info_Text":.
You also do not include a form control for the Type property so if ModelState is invalid and you return the view, your code would fail (the value of Type will be null) so you either need to include a hidden input or call the database again to get the values before returning the view.
Related
I'm new to ASP.NET MVC; I'm trying to create a dynamic table with buttons that add/remove rows when clicked using model states and not jQuery, so right now my page words but I was only able to create a button that removes the last added row and I really want a way to remove a specific row from my table by adding the "remove" option to table data.
Here's the code I have right now that is working and removing the last added row.
View
#{ for (int i = 0; i < Model.ValueModel.Count; i++)
{
<div>
<table>
<tr id="Row">
<td>
#Html.DropDownListFor(m => m.ValueModel[i].id, new SelectList(Model.ReplaceModel, "id", "Name"))
</td>
<td>
#Html.EditorFor(m => m.ValueModel[i].value)
</td>
</tr>
</table>
</div>
}
}
<button name="submit" value="AddLine"> Add Line </button>
<button name="submit" value="RemLine"> Remove Line </button>
Controller
[HttpPost]
public ActionResult Test(TestModel model, string submit)
{
switch (submit)
{
case "AddLine":
model.ValueModel.Add(new ValueModel());
break;
case "RemLine":
if (model.ValueModel.Count > 0)
{
model.ValueModel.RemoveAt(model.ValueModel.Count - 1);
}
}
return View(model);
}
The code works with my models which are not important for the question what I want to learn is a way to remove the line by adding it to a third <td> in the view and using a similar action to
model.ValueModel.RemoveAt(model.ValueModel.Count - 1);
from the switch case "RemLine".
Something like this
#{ for (int i = 0; i < Model.ValueModel.Count; i++)
{
<div>
<table>
<tr id="Row">
<td>
#Html.DropDownListFor(m => m.ValueModel[i].id, new SelectList(Model.ReplaceModel, "id", "Name"))
</td>
<td>
#Html.EditorFor(m => m.ValueModel[i].value)
</td>
<td>
<button name="submit" value="RemLine"> Remove Line </button>
</td>
</tr>
</table>
</div>
}
}
<button name="submit" value="AddLine"> Add Line </button>
Controller
[HttpPost]
public ActionResult Test(TestModel model, string submit)
{
switch (submit)
{
case "AddLine":
model.ValueModel.Add(new ValueModel());
break;
case "RemLine":
if (model.ValueModel.Count > 0)
{
model.ValueModel.**BegoneLine**(The line in which the button is);
}
}
return View(model);
}
Thanks in advance!
You need to send a row id with your 'remove' button and then read it in your controller. You can add buttons like this (per row):
<button name="submit" value="#(i)">Remove row</button>
...
<button name="submit" value="#(i)">Remove row</button>
Controller:
public ActionResult Test(TestModel model, string submit)
{
switch (submit)
{
case "AddLine":
model.ValueModel.Add(new ValueModel());
break;
case "RemLine":
if (model.ValueModel.Count > 0)
{
model.ValueModel.RemoveAt(model.ValueModel.Count - 1);
}
break;
case string number when int.TryParse(number,out var index):
if (model.ValueModel.Count > 0)
{
model.ValueModel.RemoveAt(index);
}
break;
}
I'm developing an ASP.NET MVC 5 web with C# and .NET Framework 4.5.1.
I have this form in a cshtml file:
#model MyProduct.Web.API.Models.ConnectBatchProductViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Create</title>
</head>
<body>
#if (#Model != null)
{
<h4>Producto: #Model.Product.ProductCode, Cantidad: #Model.ExternalCodesForThisProduct</h4>
using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
{
#Html.HiddenFor(model => model.Product.Id, new { #id = "productId", #Name = "productId" });
<div>
<table id ="batchTable" class="order-list">
<thead>
<tr>
<td>Cantidad</td>
<td>Lote</td>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].Quantity")</td>
<td>#Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].BatchName")</td>
<td><a class="deleteRow"></a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: left;">
<input type="button" id="addrow" value="Add Row" />
</td>
</tr>
</tfoot>
</table>
</div>
<p><input type="submit" value="Seleccionar" /></p>
}
}
else
{
<div>Error.</div>
}
<script src="~/Scripts/jquery-2.1.3.min.js"></script>
<script src="~/js/createBatches.js"></script> <!-- Resource jQuery -->
</body>
</html>
And this is the action method:
[HttpPost]
public ActionResult Save(FormCollection form)
{
return null;
}
And the two ViewModel:
public class BatchProductViewModel
{
public int Quantity { get; set; }
public string BatchName { get; set; }
}
public class ConnectBatchProductViewModel
{
public Models.Products Product { get; set; }
public int ExternalCodesForThisProduct { get; set; }
public IEnumerable<BatchProductViewModel> BatchProducts { get; set; }
}
But I get this in FormCollection form var:
But I want to get an IEnumerable<BatchProductViewModel> model:
public ActionResult Save(int productId, IEnumerable<BatchProductViewModel> model);
If I use the above method signature both parameters are null.
I want an IEnumerable because user is going to add more rows dynamically using jQuery.
This is jQuery script:
jQuery(document).ready(function ($) {
var counter = 0;
$("#addrow").on("click", function () {
counter = $('#batchTable tr').length - 2;
var newRow = $("<tr>");
var cols = "";
var quantity = 'ConnectBatchProductViewModel.BatchProducts[0].Quantity'.replace(/\[.{1}\]/, '[' + counter + ']');
var batchName = 'ConnectBatchProductViewModel.BatchProducts[0].BatchName'.replace(/\[.{1}\]/, '[' + counter + ']');
cols += '<td><input type="text" name="' + quantity + '"/></td>';
cols += '<td><input type="text" name="' + batchName + '"/></td>';
cols += '<td><input type="button" class="ibtnDel" value="Delete"></td>';
newRow.append(cols);
$("table.order-list").append(newRow);
counter++;
});
$("table.order-list").on("click", ".ibtnDel", function (event) {
$(this).closest("tr").remove();
counter -= 1
$('#addrow').attr('disabled', false).prop('value', "Add Row");
});
});
Any idea?
I have checked this SO answer, and this article but I don't get my code working.
You need to generate the controls for the collection in a for loop so they are correctly named with indexers (note that property BatchProducts needs to be IList<BatchProductViewModel>
#using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
{
....
<table>
....
#for(int i = 0; i < Model.BatchProducts.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => m.BatchProducts[i].Quantity)</td>
<td>#Html.TextBoxFor(m => m.BatchProducts[i].BatchName)</td>
<td>
// add the following to allow for dynamically deleting items in the view
<input type="hidden" name="BatchProducts.Index" value="#i" />
<a class="deleteRow"></a>
</td>
</tr>
}
....
</table>
....
}
Then the POST method needs to be
public ActionResult Save(ConnectBatchProductViewModel model)
{
....
}
Edit
Note: Further to your edit, if you want to dynamically add and remove BatchProductViewModel items in he view, you will need to use the BeginCollectionItem helper or a html template as discussed in this answer
The template to dynamically add new items would be
<div id="NewBatchProduct" style="display:none">
<tr>
<td><input type="text" name="BatchProducts[#].Quantity" value /></td>
<td><input type="text" name="BatchProducts[#].BatchName" value /></td>
<td>
<input type="hidden" name="BatchProducts.Index" value ="%"/>
<a class="deleteRow"></a>
</td>
</tr>
</div>
Note the dummy indexers and the non-matching value for the hidden input prevents this template posting back.
Then the script to add a new BatchProducts would be
$("#addrow").click(function() {
var index = (new Date()).getTime(); // unique indexer
var clone = $('#NewBatchProduct').clone(); // clone the BatchProducts item
// Update the index of the clone
clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
clone.html($(clone).html().replace(/"%"/g, '"' + index + '"'));
$("table.order-list").append(clone.html());
});
In your Post Methode you receive "MyProduct.Web.API.Models.ConnectBatchProductViewModel" as Parameter.
Use the existing model for the Post methode.
Why do you want a IEnumerable from your model? there is only one available including the id in the model.
you can visit this article for complete source code with a video tutorial.
you have to create an action first, from where we can pass the list of object
[HttpGet]
public ActionResult Index()
{
List<Contact> model = new List<Contact>();
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
model = dc.Contacts.ToList();
}
return View(model);
}
then we need to create a view for that action
#model List<UpdateMultiRecord.Contact>
#{
ViewBag.Title = "Update multiple row at once Using MVC 4 and EF ";
}
#using (#Html.BeginForm("Index","Home", FormMethod.Post))
{
<table>
<tr>
<th></th>
<th>Contact Person</th>
<th>Contact No</th>
<th>Email ID</th>
</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td> #Html.HiddenFor(model => model[i].ContactID)</td>
<td>#Html.EditorFor(model => model[i].ContactPerson)</td>
<td>#Html.EditorFor(model => model[i].Contactno)</td>
<td>#Html.EditorFor(model => model[i].EmailID)</td>
</tr>
}
</table>
<p><input type="submit" value="Save" /></p>
<p style="color:green; font-size:12px;">
#ViewBag.Message
</p>
}
#section Scripts{
#Scripts.Render("~/bundles/jqueryval")
}
and then we have to write code for save the list of object to the database
[HttpPost]
public ActionResult Index(List<Contact> list)
{
if (ModelState.IsValid)
{
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
foreach (var i in list)
{
var c = dc.Contacts.Where(a =>a.ContactID.Equals(i.ContactID)).FirstOrDefault();
if (c != null)
{
c.ContactPerson = i.ContactPerson;
c.Contactno = i.Contactno;
c.EmailID = i.EmailID;
}
}
dc.SaveChanges();
}
ViewBag.Message = "Successfully Updated.";
return View(list);
}
else
{
ViewBag.Message = "Failed ! Please try again.";
return View(list);
}
}
using(Html.BeginForm())
{
// code here
}
While to Post form Data all tags must be included form tag.
Following the principle of DRY, you can create one EditorTemplate for that purpose.
Steps:
1- In Views > Shared > Create new folder named (EditorTemplates)
2- Create a view inside your newly created EditorTemplates folder , the view's model should be BatchProductViewModel according to the OP example. Place your code inside the Editor view. No loop or index is required.
An EditorTemplate will act similar to a PartialView for every child entity but in a more generic way.
3- In your parent entity's view, call your Editor :
#Html.EditorFor(m => m.BatchProducts)
Not only this provides a more organized views, but also let's you re-use the same editor in other views as well.
i want to bring the partial view of question link list to my edit view. However, when i select the groupby value, it shows the error below. Please guide me.
The model item passed into the dictionary is of type
'System.Collections.Generic.List1[<>f__AnonymousType42[SurveyTool.Models.SURV_Question_Ext_Model,SurveyTool.Models.SURV_Question_Model]]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`1[SurveyTool.Models.SURV_Question_Ext_Model]'.
Edit view:
#model IFXSurveyTool.Models.SURV_Main_Model
<div class="question">
<h2>Link</h2>
#Html.Action("QuestionLink", "SURV_Main", new { Survey_ID = Model.Survey_ID })
</div>
Controller:
public ActionResult QuestionLink(int Survey_ID)
{
var query = from r in db.SURV_Question_Ext_Model
join s in db.SURV_Question_Model on r.Qext_Question_ID equals s.Question_ID
where s.Question_Survey_ID == Survey_ID
group new { r, s } by r.Qext_Language into grp
select grp.FirstOrDefault();
return PartialView(query.ToList());
}
QuestionLink view:
#model IEnumerable<SurveyTool.Models.SURV_Question_Ext_Model>
<br />
<table class="strip">
#foreach (var item in Model)
{
<tr>
<td width="5%"></td>
<td>
#Html.ActionLink("QuestionLink", "Edit", "SURV_Answer", new { Language = item.Qext_Language }, null)
</td>
</tr>
}
</table>
Please change following line in Your PartialView code:
#model IEnumerable<SurveyTool.Models.SURV_Question_Ext_Model>
to:
#model List<SurveyTool.Models.SURV_Question_Ext_Model>
Or returning type in controller. Types needs match.
From comments:
there is also returned group of LINQ query, so it's anoumous Type, not the one You expecting.
You have little error. Try this:
#model IEnumerable<SurveyTool.Models.SURV_Question_Ext_Model>
<br />
<table class="strip">
#foreach (var item in Model)
{
<tr>
<td width="5%"></td>
<td>
#Html.ActionLink("Edit", "QuestionLink", "SURV_Answer", new { Language = item.Qext_Language }, null)
</td>
</tr>
}
I have this view based on a list of a model where I create strongly-typed checkboxes for each items of the model based on a boolean.
Here's my view:
#using MyApp.Models
#model IList<MyApp.Models.ObjInfo>
#{
ViewBag.Title = "Obj Inventory";
}
<h2>Search Inventory</h2>
<p>
#using (Html.BeginForm())
{
(Many search filters which are non-relevant)
<p>
Send Items: #Html.ActionLink("Click Here", "SendItems")
</p>
}
<table>
<tr>
<th>
Obj Name
</th>
<th>
Number In Stock
</th>
(...)
<th>
Select Item
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.OtherObj.m_Name)
</td>
(...)
<td>
#Html.CheckBoxFor(modelItem => item.m_IsSelected)
</td>
</tr>
}
</table>
The whole process works fine and I can actually generate a view with checkboxes for each item of my list of model.
Now my question is that I want to create a list which would regroup only the items in the list which are checked and send them to the controller. How could I do that? Can anyone help me or suggest me a way to work?
Thank you!
* EDIT *
Here is the HttpPost Method used to get the List of items as mentioned below:
//
// GET: /Inventory/SendItems
[HttpPost]
public ActionResult SendItems(IList<ObjInfo> listToSend)
{
m_ListObjToSend = new List<ObjInfo>();
foreach (var item in listToSend.Where(item => item.m_IsSelected))
{
m_ListObjToSend .Add(item);
}
return View(m_ListObjToSend );
}
However I have encountered many problems:
This method does NOT work if I put the [HttpPost] attribute (it will show as "Not Found");
The list I am supposed to receive is null;
Each hiddenfield linked with the checkbox has default value as false even if the checked value shows true;
I am using an actionlink because I do not want to use a button, there is already one that is doing another job.
I am open for any comments / help available, thank you!
If you use the CheckBoxFor helper to generate checkboxes you will notice that it generates an additional hidden field along with each checkbox. This means that all values will be sent to the controller and you will have to filter in your controller those that are checked.
Also I would recommend you using indexes to ensure proper model binding. You just need to use an IList<ObjInfo> or ObjInfo[] which is trivially easy achievable by calling .ToList() or .ToArray() extension methods on your view model before passing it to the view:
#using MyApp.Models
#model IList<ObjInfo>
...
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => x[i].OtherObj.m_Name)
</td>
(...)
<td>
#Html.CheckBoxFor(x => x[i].m_IsSelected)
</td>
</tr>
}
...
And now your controller action could directly take the list of items:
[HttpPost]
public ActionResult SomeAction(IEnumerable<ObjInfo> model)
{
...
}
and if you wanted to find the selected values, you could simply get them through LINQ:
[HttpPost]
public ActionResult SomeAction(IEnumerable<ObjInfo> model)
{
var selectedItems = model.Where(x => x.m_IsSelected);
...
}
Remark: m_Name and m_IsSelected is a disastrously bad naming convention for a properties in C#.
UPDATE:
Another issue you have with your code is that your Html.BeginForm doesn't contain any input field. It has only a single ActionLink which obviously only does a GET request. If you want to submit the values you should wrap your entire table with the form and use a submit button and not some action links:
#using MyApp.Models
#model IList<ObjInfo>
#{
ViewBag.Title = "Obj Inventory";
}
<h2>Search Inventory</h2>
<p>
#using (Html.BeginForm("SendItems", null, FormMethod.Post))
{
(Many search filters which are non-relevant)
<table>
<tr>
<th>Obj Name</th>
<th>Number In Stock</th>
(...)
<th>Select Item</th>
</tr>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
<!--
This will not be sent to your controller because it's only a label.
You will need a corresponding hidden field if you want to get that value back
-->
#Html.DisplayFor(x => x[i].OtherObj.m_Name)
</td>
(...)
<td>
#Html.CheckBoxFor(x => x[i].m_IsSelected)
</td>
</tr>
}
</table>
<p>
Send Items: <button type="submit">Click Here</button>
</p>
}
</p>
So really, 2 things you should learn:
The naming convention that the default model binder expects when binding to a list
How to use a javascript debugging tool (such as FireBug and/or Chrome Developper Toolbar) which will allow you to inspect all the values that are sent to your server and immediately recognized whether you respected the convention you learned in 1.
I'm trying to build a dynamically generated view. My controller class for Create action looks like this
public ActionResult Create()
{
List<FormMetadata> formItems = GetFormItems();
return View(formItems);
}
and the View so far is something like this
<% using (Html.BeginForm())
{%>
<table>
<% foreach (var item in Model)
{
if (!item.IsActive)
{
continue;
}
%>
<tr>
<td>
<%=Html.Encode(item.DisplayValue)%>
</td>
<td>
<%
if (item.FieldType == "TextBox")
{%>
<%=Html.TextBox(item.Field, null, new { tabindex = item.SortOrder })%>
<%}
if (item.FieldType == "CheckBox")
{%>
<%=Html.CheckBox(item.Field, false, new { tabindex = item.SortOrder })%>
<%}
%>
</td>
<td>
</td>
</tr>
<%} %>
</table>
I want to show the same view with the values retained when there are validation errors. Code like the following is used to catch the validation errors
if (string.IsNullOrEmpty(collection[item.ToString()]))
{
ModelState.AddModelError(key, "Required.");
}
How can I show a view with validation errors while retaining the values which have been entered for this scenario?
I'd be checking out NerdDinner first and basically using the same approach.
EDIT2
I have created a quick sample project which works. There is one thing I do not like and that is that I cannot pass the list itself around. I have to create the blank list every time and read all the values from the textboxes and save this in the list and give this updated list to the new view. Next round same thing. But it works.
Basically:
public ActionResult About() {
List<FormMetaData> formItems = GetFormItems();
return View(formItems);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult About(FormCollection form)
{
List<FormMetaData> formItems = GetFormItems();
//TryUpdateModel(formItems);
// update project members
foreach (var key in form.AllKeys) {
if (key.ToString().StartsWith("TextBox")) {
string field = (key.ToString().Replace("TextBox", ""));
if (!string.IsNullOrEmpty(form.Get(key.ToString()))) {
formItems.Find(delegate(FormMetaData t) { return t.Field == field; }).Value = form.Get(key.ToString());
}
else { }
// this.ProjectRepository.DeleteMemberFromProject(id, userId);
}
}
ModelState.AddModelError("test", "this is a test error");
if(ModelState.IsValid)
{
///
}
else
{
return View(formItems);
}
return View(formItems);
}
private List<FormMetaData> GetFormItems() {
List<FormMetaData> output = new List<FormMetaData>();
FormMetaData temp1 = new FormMetaData("TextBox",true,"temp1","displayText1");
FormMetaData temp2 = new FormMetaData("TextBox", true, "temp2", "displayText2");
output.Add(temp1);
output.Add(temp2);
return output;
}
and then you have your view:
<% using (Html.BeginForm()) {%>
<table>
<% foreach (var item in Model) {
if (!item.isActive) {
continue;
} %>
<tr>
<td>
<%=Html.Encode(item.DisplayValue)%>
</td>
<td>
<% if (item.FieldType == "TextBox") {%>
<%=Html.TextBox("TextBox"+item.Field, item.Value)%>
<%} if (item.FieldType == "CheckBox") {%>
<%=Html.CheckBox("Value")%>
<%}%>
</td>
<td>
</td>
</tr>
<%} %>
<p>
<input type="submit" value="submit" />
</p>
<% } %>
</table>
I have uploaded a zipfile for you # http://www.bastijn.nl/zooi/dynamicSample.rar
EDIT
I have tried this example and it goes wrong with the modelbinder. When I use "FormCollection form" as input to the POST create method the values of my textboxes are there under the key supplied. So you have to either your custom model binder or make a model which will work with the default model binder.
To be more specific. It goes wrong because in this case your textboxes are updating properties in objects inside the List, which is the Model passed. Normally your textboxes are updating properties inside the Object which is also your Model and the key used for the textbox (for automatic modelbinding) is the name of the property you update.
So I suppose the model binder does not bind the values in the textbox to your items in the list since it simply does not know how to do this automatically. It is 3.17 am here right now so I'm off to bed, the question is interesting and I might finish the answer tomorrow.
original
<%=Html.TextBox(item.Field, null, new { tabindex = item.SortOrder })%>
It seems you are generating your form every time with the values set to null.
Try to init them something like:
<%=Html.TextBox(item.Field, **item.Value**, new { tabindex = item.SortOrder })%>
And in your controller, when you check for ModelState.isValid do something like:
if(ModelState.isValid){
//code when it works
}
else{
return View(formItems) // these should contain the just added values
}
That should do the trick.
So, in a simple example you get something like:
public ActionResult Create()
{
List<FormMetadata> formItems = GetFormItems();
return View(formItems);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(List<FormMetadata> formItems)
{
if(ModelState.isValid)
{
MyUpdate()
save()
}
else
{
return view(formItems)
}
}
And your view:
<%
if (item.FieldType == "TextBox")
{%>
<%=Html.TextBox(item.Field, **item.Value**, new { tabindex = item.SortOrder })%>
<%}
if (item.FieldType == "CheckBox")
{%>
<%=Html.CheckBox(item.Field, **item.Value**, new { tabindex = item.SortOrder })%>
<%}
I'm using the following approach now, only working with Textboxes at the moment
The View has
<%
for (int i = 0; i < Model.Count; i++)
{
var name = "formItems[" + i + "].Field";
var htmlAttributes = new Dictionary<string, object>
{
{"tabindex", Model[i].SortOrder},
{"class", Model[i].ClientSideValidation}
};
%>
<div> <%=Html.Encode(Model[i].DisplayValue)%>
<%=Html.TextBox(name, Model[i].DefaultValue, htmlAttributes)%>
<%= Html.ValidationMessage(Model[i].Field) %>
</div>
<% } %>
And the controller Action methods
GET
public ActionResult Create()
{
List<FormMetadata> formItems = GetFormItems();
HttpContext.Cache[FormCacheKey] = formItems;
return View(formItems);
}
POST (part of the code)
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(List<FormMetadata> formItems)
{
var formDefinition = new List<FormMetadata>();
try
{
if (HttpContext.Cache[FormCacheKey] != null)
{
formDefinition = HttpContext.Cache[FormCacheKey] as List<FormMetadata>;
}
else
{
formDefinition = GetFormItems();
HttpContext.Cache[FormCacheKey] = formItems;
}
var formValues = new Dictionary<string, string>();
for (int i = 0; i < formDefinition.Count; i++)
{
var key = formDefinition[i].Field;
var value = formItems[i].Field ?? string.Empty;