Reputation: 225
I have taken an issue that I am having with a asp.net mvc4 applications and simplified it so I can post it here. My basic problem is that I am trying to send a list of items to my view and edit the check box of any of the items and then send the list items back to the controller and be able to eventually save to the database. The way the code is written right now when I send the list back to the controller it comes across as a null value just like it was never instantiated.
The code is as follows:
Public Class Person
Property ID As Integer
Property Name As String
Property Active As Boolean
End Class
In the controller I call a class called BuildPeople that is really just a way to build the the list to pass around:
Public Class BuildPeople
Public Function GetPersonList() As List(Of Person)
Dim personList As New List(Of Person)
personList.Add(GetPerson(1, "Chris", True))
personList.Add(GetPerson(2, "Ken", True))
personList.Add(GetPerson(3, "Jen", True))
Return personList
End Function
Private Function GetPerson(id As Integer, name As String, active As Boolean) As Person
Dim p As New Person
p.ID = id
p.Name = name
p.Active = active
Return p
End Function
End Class
The controller only has the edit ability:
Imports System.Web.Mvc
Public Class PeopleController
Inherits Controller
' GET: /People
Function Index() As ActionResult
Return View()
End Function
' GET: /People/Edit/5
Function Edit() As ActionResult
Dim bp As New BuildPeople
Dim model As New List(Of Person)
model = bp.GetPersonList
ViewData.Model = model
Return View()
End Function
' POST: /People/Edit/5
<HttpPost()>
Function Edit(ByVal listPeople As List(Of Person)) As ActionResult
Try
' TODO: Add update logic here
If listPeople Is Nothing Then
'Don't want to end up here
Return View()
Else
'Want to end up here
Dim i As Integer = listPeople.Count
Return View()
End If
Catch
Return View()
End Try
End Function
End Class
And then the view is as follows:
@ModelType List(Of Person)
@Code
ViewData("Title") = "Edit"
Layout = "~/Views/Shared/_Layout.vbhtml"
End Code
<h2>Edit</h2>
@Using (Html.BeginForm())
@Html.AntiForgeryToken()
@<div class="form-horizontal">
<h4>Person</h4>
<hr />
@For Each item In Model
Dim currentitem = item
@Html.HiddenFor(Function(model) currentitem.ID)
@Html.EditorFor(Function(model) currentitem.Name)
@Html.EditorFor(Function(model) currentitem.Active)
Next
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
End Using
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Upvotes: 2
Views: 1494
Reputation: 33738
So you have to have some understanding of how HTML forms work as well as how the MVC model binder works.
Checkboxes only send a value back in the post data if they are checked.
Next, the model binder in MVC will reconstitute collection/list objects as long as the field naming follows the proper naming convention.
So your loop For Each item In Model
needs to produce items with the correct name.
Let's change your model slightly.
Public Class PeopleModel
Public Property People As List(Of Person)
Public Property SelectedPeople As List(Of Int64) ' Assuming Person.ID is Int64
End Class
Then your change your view's loop like so:
Dim itemIndex As Int32 = 0
For Each person As Person In Model.People
@<input type='checkbox' name='SelectedPeople[@itemIndex]' id='[email protected]' value='@person.ID' />
@<input type='hidden' name='SelectedPeople[@itemIndex]' value='-1' />
itemIndex += 1
Next
We place the hidden element in there because it will provide a field value for unchecked items. Otherwise the first unchecked item would break the indices and the model binder would stop.
Now in your controller's post handler:
<HttpPost>
Public Function ActionName(model As PeopleModel) As ActionResult
For Each id As Int64 In model.SelectedPeople
If 0 < id Then
' This is the id of a selected person - do something with it.
End If
Next
End Function
Upvotes: 3