I am sure I am missing something very obvious, or I am not understanding what I have read so far. I have a from that contains a table of data, 2 fields of which need to be editable. The data received by the form is IEnumerable. However, when the controller function receiving the post data, instead of it receiving an IEnumerable, I get nothing. If I receive just the raw data type, I get the a single instance of the object with the correct id field and all other fields are empty. Could someone please point me oin the right direction?
MODEL: (Generated by EF Model first)
Partial Public Class QuoteBundlePackage_Result
Public Property id As Integer
Public Property employeeId As Nullable(Of Integer)
Public Property employeeName As String
Property bundleId As Nullable(Of Integer)
Public Property bundleDescription As String
Public Property packageId As Nullable(Of Integer)
Public Property packageContents As String
End Class
View:
#ModelType IEnumerable(Of gbip_new.QuoteBundlePackage_Result)
#Using Html.BeginForm()
#Html.ValidationSummary(True)
<fieldset>
<legend>Quote</legend>
<table>
<tr>
<th>
#Html.DisplayNameFor(Function(model) model.employeeId)
</th>
<th>
#Html.DisplayNameFor(Function(model) model.employeeName)
</th>
<th>
#Html.DisplayNameFor(Function(model) model.bundleId)
</th>
<th>
#Html.DisplayNameFor(Function(model) model.bundleDescription)
</th>
<th>
#Html.DisplayNameFor(Function(model) model.packageId)
</th>
<th>
#Html.DisplayNameFor(Function(model) model.packageContents)
</th>
<th></th>
</tr>
#For Each item In Model
Dim currentItem = item
Html.HiddenFor(Function(modelItem) currentItem.id)
#<tr>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.employeeId)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.employeeName)
</td>
<td>
#Html.EditorFor(Function(modelItem) currentItem.bundleId)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.bundleDescription)
</td>
<td>
#Html.EditorFor(Function(modelItem) currentItem.packageId)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.packageContents)
</td>
</tr>
Next
</table>
<p>
<input id="Submit1" type="submit" value="submit" />
</p>
</fieldset>
End Using
Controller
<HttpPost()> _
Function QuoteBundlePackage(ByVal eqDetails As IEnumerable(Of Global.gbip_new.QuoteBundlePackage_Result)) As ActionResult
If ModelState.IsValid Then
'Do stuff
Return RedirectToAction("Index")
End If
Return View(eqDetails)
End Function
Model binding to a collection works a little differently. You need to effectively number each item you are looping through if you want your collection to be bound correctly.
What you want to render is something like...
<input type="text" name="QuoteBundlePackage_Result[0].EmployeeID" value="" />
<input type="text" name="QuoteBundlePackage_Result[0].EmployeeName" value="" />
<input type="text" name="QuoteBundlePackage_Result[1].EmployeeID" value="" />
<input type="text" name="QuoteBundlePackage_Result[1].EmployeeName" value="" />
... which will allow the framework to distinguish between each item. To create this arrangement you should give each item an ID within your loop - (Sorry in advance for answer being in c# and not vb!)
#for (int i = 0; i < Model.Count(); i++)
{
#Html.EditorFor(e => e[i].EmployeeID)
#Html.EditorFor(e => e[i].EmployeeName)
}
See related articles from Scott Hansleman and Phil Haack.
Related
i have a problem to collect the values in my text fields .
in each row on my screen i have a text field and the user should be add values there (you can see it from the picture)
exaple of my site
this is my html code:
#using (Html.BeginForm("Create", "User", FormMethod.Post))
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.BarCode)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Profit)
</th>
<th>
Amount
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.BarCode)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Profit)
</td>
<td>
<form action="/action_page.php">
Amount:
<input type="text" id="amountTag" name="Amount" maxlength="2" placeholder="0" size="4" runat="server" />
</form>
</td>
</tr>
}
<tr>
<td><input type="submit" value="Submit" class="btn btn-default" /></td>
</tr>
</table>
}
and my code is
// POST: User/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
/*Update the Database*/
// TODO: Add insert logic here
string x = Request.Form["amountTag"];
return RedirectToAction("Index");
}
catch
{
return View();
}
}
this code always get Null and i don't have idea why
This section will create a form inside existing form (nested forms), which is not a good way to bind the textbox:
<form action="/action_page.php">
Amount:
<input type="text" id="amountTag" name="Amount" maxlength="2" placeholder="0" size="4" runat="server" />
</form>
The correct way should be like this:
1) Create an int property of Amount in the same viewmodel class as BarCode, Name and Profit properties has.
public class ViewModelName
{
// required unique key for each rows
public int Id { get; set; }
// other 3 properties here
[DisplayName("Amount")]
public int Amount { get; set; }
}
2) Bind the model inside view with HTML helpers using for loop. Here you should add TextBoxFor helper for Amount property to let user input numeric value with HTML attributes you have previously:
#model IEnumerable<ViewModelClassName>
#using (Html.BeginForm("Create", "User", FormMethod.Post))
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.BarCode)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Profit)
</th>
<th>
Amount
</th>
</tr>
#for (int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(modelItem => modelItem[i].Id)
<tr>
<td>
#Html.DisplayFor(modelItem => modelItem[i].BarCode)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem[i].Name)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem[i].Profit)
</td>
<td>
#* Amount *#
#Html.LabelFor(modelItem => modelItem[i].Amount)
#Html.TextBoxFor(modelItem => modelItem[i].Amount, new { maxlength = 2, ... })
</td>
</tr>
}
<tr>
<td><input type="submit" value="Submit" class="btn btn-default" /></td>
</tr>
</table>
}
3) Make sure that action method decorated with HttpPostAttribute has list of viewmodel class as parameter.
[HttpPost]
public ActionResult Create(List<ViewModelName> model)
{
try
{
// example to select all amount values
var x = model.Select(x => x.Amount).ToList();
// do something
return RedirectToAction("Index");
}
catch
{
// error handling
return View(model);
}
}
From this point, your model binding should working fine.
Related issue: Need to pass a List<model> to the Http Post in a Controller
Can I pass table td values to controller?
View strongly typed:
#using (Html.BeginForm("PostClick", "Vendor", FormMethod.Post)) {
<table class="tblData">
<tr>
<th>
#Html.DisplayNameFor(model => model.First().SubmittedDate)
</th>
<th>
#Html.DisplayNameFor(model => model.First().StartDate)
</th>
</tr>
<tr>
<td>
#Html.DisplayFor(modelItem => item.SubmittedDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.StartDate)
</td>
</tr>
</table>
<input type="submit" value="submit" />
}
Contoller code:
public void PostClick(FormCollection collection)
{
/*Some Code */
}
How to pass table value from view to controller?
Have used JasonData & Ajax call and able to send the table data to controller.
Want to know any other method can be done because FormCollection data not able to find table values
Your need to generate controls that post back (input, textarea or select) and generate those controls in a for loop (or use a custom EditorTemplate for type Vendor)
View
#model List<Vendor>
#using (Html.BeginForm())
{
<table class="tblData">
<thead>
....
</thead>
<tbody>
for(int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => m[i].SubmittedDate)</td>
<td>#Html.TextBoxFor(m => m[i].StartDate)</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="submit" />
}
Post method
public void PostClick(List<Vendor> model)
{
/*Some Code */
}
I have this model
public class ItemClassification
{
public int ItemID {get; set;}
public string ItemName {get; set;}
}
and I have this form
<form method="POST" action="/Home/UpdateItemClassifications">
<table>
<tr>
<th>Item ID</th>
<th>Item Name</th>
</tr>
<tr>
<td>
<input name="ItemClassification[0].ItemID" value="1"/>
</td>
<td>
<input name="ItemClassification[0].ItemName" value="Item One"/>
</td>
</tr>
<tr>
<td>
<input name="ItemClassification[1].ItemID" value="2"/>
</td>
<td>
<input name="ItemClassification[1].ItemName" value="Item Two"/>
</td>
</tr>
<tr>
<td>
<input name="ItemClassification[2].ItemID" value="3"/>
</td>
<td>
<input name="ItemClassification[2].ItemName" value="Item Three"/>
</td>
</tr>
</table>
</form>
And I'm trying to bind these values to this controller action to loop through them and update them in the db.
[HttpPost]
public ActionResult UpdateItemClassifications(List<ItemClassification> UpdatedClassifications)
{
//save logic here..
}
For whatever reason I am not getting my posted form values bound to the ItemClassification model. Any help or direction would be appreciated. I have tried posting this form with only 1 set of values and removed the "List<>" from the controller and it mapped the values correctly.
Turns out I didn't need the "ItemClassification" before the [0].ItemID, thank you for the answer Sundeep. I may indeed need to look at the hacked article you referenced since I may be adding a JavaScript delete function to this and it will no doubt change my indexing.
I have a view in my app that uses a #modeltype ienumerable of (xxxxxxx.abc), and uses a for each loop to populate the view. This view has select boxes that show up next to each item in the view. When I post the form to the controller accepting it as a formcollection does not work, and if I accept it like: ByVal abc as abc it says that its an invalid model.. The code is as follows:
<AcceptVerbs(HttpVerbs.Post)>
Function ClassAttendance(ByVal Attendance As Attendance) As ActionResult
If ModelState.IsValid Then
UpdateModel(Attendance)
db.SaveChanges()
Return RedirectToAction("Index")
End If
Return View()
End Function
Any ideas?? Can I somehow use a for each loop in the view without making it ienumerable? If so when It posts to the controller my below code would just about work.. The only value that really matters is the selectlist choice and the id of the record that it was changed for...
The view is:
#ModelTYPE List(Of xxxxx.attendance)
#Code
ViewData("Title") = "Class Attendance Record"
End Code
#Using Html.BeginForm
#<fieldset>
<table>
<tr>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Registrant ID
</th>
<th>
Course Status
</th>
<th>
Comments
</th>
</tr>
#For r As Integer = 0 To ViewBag.count
Dim i As Integer = r
#Html.HiddenFor(Function(m) m(i).id)
#<tr>
<td>
#Html.DisplayFor(Function(m) m(i).firstName)
</td>
<td>
#Html.DisplayFor(Function(m) m(i).lastName)
</td>
<td>
#Html.DisplayFor(Function(m) m(i).reg_id)
</td>
<td>
#Html.DisplayFor(Function(m) m(i).Completed_Class)
</td>
<td>
#Html.DropDownList("Completed_Class", New SelectList(ViewBag.courseStatus, "Status", "Status"))
</td>
<td>
#Html.TextBoxFor(Function(m) m(i).Comments, New With {.class = "AttenComment"})
</td>
</tr>
Next
</table>
<p>
<input type="submit" name="submit" />
</p>
</fieldset>
End Using
I have been over countless tut's and posts with no luck at all... I only want to update the model record for each record to the corresponding selected value from the selectlist... Thanks greatly for any and all help..
In brief, this can be done. There are many ways to accomplish this, the easiest by far I found was to have a custom view model which contains an array of the model you want to bulk insert.
public class MultipleAttendance
{
public Attendance[] attendances {get;set;}
}
Then in your View you can loop as such:
#for(int i=0,i<10;i++)
{
#Html.TextBox(attendances[i].firstName)
}
In the controller
[HttpPost]
public ActionResult Create(MultipleAttendance model)
{
// Get a value
model.attendances[0].firstName;
}
Hope it helps :)
Hi I have a view which displays Invoices and InvoiceLines.
#model VectorCheck.ViewModels.InvoiceViewModel
#{
ViewBag.Title = "Invoice Details";
}
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/EditorHookup.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<fieldset>
<legend>Invoice</legend>
<table>
<tr>
<th>
Activity ID
</th>
<th>
Invoice Line Amount
</th>
<th>
Payment Type
</th>
<th>
Note
</th>
<th>
</th>
<th>
</th>
<th>
</th>
</tr>
#foreach (var item in Model.InvoiceLines) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Activity.Descriptor)
</td>
<td>
#Html.DisplayFor(modelItem => item.Amount)
</td>
<td>
#Html.DisplayFor(modelItem => item.PaymentType.Name)
</td>
<td>
<span>Person:</span>
#Html.DropDownListFor(modelItem => item.PersonrId, Model.People as IDictionary<string, IEnumerable<SelectListItem>>, "--- Select ---")
</td>
<td>
<input type="submit" value="Update" />
</td>
</tr>
}
}
</table>
</fieldset>
}
What I'm wanting is for each InvoiceLine without going to another screen to be able to change the value in the dropdown list for Person, click update and get this updated InvoiceLine in the controller where I can save it.
However when I get to the controller the InvoiceLine does not contain the values.
Controller method:
[HttpPost]
public ActionResult EditInvoiceLine(InvoiceLine invoiceLine, int id)
{
return View(invoiceLine);
}
Has anyone achieve anything like this on the same page or knows how to do it?
No, I do not want to use jqgrid. I have other functionality which jqgrid isn't suitable for.
InvoiceLine is empty because the controller doesn't know where it's coming from. Also, where is the 'id' coming from? Shouldn't it be 'Personid'? Easiest technique in my opinion would be just to use ajax on the button click and send values using GET through querystrings.
I would use a form per line approach (with or without AJAX). Note this will be easier if you use a non-table-based layout. At a minimum, your submit button will need to share the same table element with the input that you want to post back. Further, you could probably get by with just the line id and the person id, instead of the whole model. Use the line id to fetch the entity from the db, then update the person id and save it. Remove the surrounding form and put a form inside each table element with the dropdown list (moving the submit button as well). Modify the signature of your action to match.
#foreach (var item in Model.InvoiceLines) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Activity.Descriptor)
</td>
<td>
#Html.DisplayFor(modelItem => item.Amount)
</td>
<td>
#Html.DisplayFor(modelItem => item.PaymentType.Name)
</td>
<td>
#using (Html.BeginForm("EditInvoiceLine", new { id => modelItem.InvoiceId } ))
{
<span>Person:</span>
#Html.DropDownListFor(modelItem => item.PersonrId, Model.People as IDictionary<string, IEnumerable<SelectListItem>>, "--- Select ---")
<input type="submit" value="Update" />
}
</td>
</tr>
}
[HttpPost]
public ActionResult EditInvoiceLine( int id, int personId )
{
var line = db.InvoiceLines.SingleOrDefault( id );
line.PersonId = personId;
db.SaveChanges();
return View( line ); // more more likely a model based on the line...
}