MVC ViewModel bound list is empty

170 views Asked by At

I am using ASP.NET MVC in VB. This is the "CreateStuffViewModel", which is a view model for my "Stuff" class:

Public Property Name() As String
Public Property Description() As String
Public Property ItemNames() As List(Of String)

In the View for creating a Stuff, the model being used is the above view model. I have a dropdown combobox containing the names of all of the user's Items, which is created in the controller in the Get. The user can select an item in the combobox, then hit an "Add" button, which adds that text to a list box. When the user hits the "Create" button, the text items in the list box are supposed to be in the ItemNames list.

This is the Get function:

    ' GET: Stuff/Create
    Function Create() As ActionResult
        Dim selectItem As New List(Of SelectListItem)
        Dim curUserID = User.Identity.GetUserId()
        Dim listOfItems = db.Items.Where(Function(x) x.MyUser.Id = curUserID)
        For Each item In listOfItems
            selectItem.Add(New SelectListItem() With {.Value = item.ID, .Text = item.Name})
        Next
        ViewData("selectItem") = selectItem

        Dim newStuff As CreateStuffViewModel = New CreateStuffViewModel
        newStuff.ItemNames = New List(Of String)
        Return View(newStuff)
    End Function

Then this is the view:

@ModelType CreateStuffViewModel
@Code
    ViewData("Title") = "Create"

    'This is the list of items that was created in the controller
    Dim selectItem As New List(Of SelectListItem)
    selectItem = ViewData("selectItem")

    Dim selItems As New List(Of SelectListItem)

End Code

<h2>Create</h2>

@Using (Html.BeginForm()) 
    @Html.AntiForgeryToken()

    @<div class="form-horizontal">
         <br />
         <br />
        <h4>Create a new Stuff</h4>
        <hr />
        @Html.ValidationSummary(True, "", New With { .class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(Function(model) model.Name, htmlAttributes:= New With { .class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(Function(model) model.Name, New With { .htmlAttributes = New With { .class = "form-control" } })
                @Html.ValidationMessageFor(Function(model) model.Name, "", New With { .class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(Function(model) model.Description, htmlAttributes:= New With { .class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(Function(model) model.Description, New With { .htmlAttributes = New With { .class = "form-control" } })
                @Html.ValidationMessageFor(Function(model) model.Description, "", New With { .class = "text-danger" })
            </div>
        </div>

        @*This is the dropdown list containing the strings of item names*@
         <div class="form-group">
             <div class="control-label col-md-2" style="font-weight: bold">Select an existing Item</div>
             <div class="col-md-10" id="ItemsList">
                 @Html.DropDownList("AvailItems", selectItem)
             </div>
         </div>

         <div class="form-group">
             <div class="col-md-offset-2 col-md-10">
                <input type="button" value="Add Item" class="btn btn-default" id="AddItem"/>
             </div>
         </div>

         @*This is the list box to which the Add button adds the item text*@
         <div class="form-group">
             <div class="control-label col-md-2" style="font-weight: bold">Items in Stuff</div>
             <div class="col-md-10">
                 @Html.ListBoxFor(Function(model) model.ItemNames, New SelectList(selItems), htmlAttributes:=New With {.name = "ItemNames"})
                 <div class="btn-group-vertical">
                     <div class="btn-link">Move Up</div>
                     <div class="btn-link">Move Down</div>
                 </div>
             </div>
         </div>

         <div class="form-group">
             <div class="col-md-offset-2 col-md-10">
                 <input type="button" value="Remove Selected Item" class="btn btn-default" id="RemoveItem"/>
             </div>
         </div>

         <br />
             <div class="form-group">
                 <div class="col-md-offset-2 col-md-10">
                     <input type="submit" value="Create" class="btn btn-default" />
                 </div>
             </div>
         </div>
End Using

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@Section Scripts 
    @Scripts.Render("~/bundles/jqueryval")

    @*Script for adding selected item from drop down to the list*@
    <script>
        $('#AddItem').click(function () {
            var selection = $('#ItemsList :selected').text();
            selection.trim();
            var x = document.getElementsByName("ItemNames");
            var i;
            for (i = 0; i < x.length; i++) {
                if (x[i].type == "select-multiple") {  //listbox
                    x[i].options.add(new Option(selection, selection));
                }
            }
        });
    </script>

    @*Script for removing selected item from list*@
    <script>
        $('#RemoveItem').click(function () {
            var x = document.getElementsByName("ItemNames");
            var listBox;
            var i;
            for (i = 0; i < x.length; i++) {
                if (x[i].type == "select-multiple") {  //listbox
                    listBox = x[i];
                }
            }
            if (listBox.selectedIndex >= 0) {
                var sel = listBox.options[listBox.selectedIndex];
                listBox.options.remove(sel);
            }
        });
    </script>

End Section

And this is the Post function:

    ' POST: Stuff/Create
    'To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    <HttpPost()>
    <ValidateAntiForgeryToken()>
    Async Function Create(ByVal model As CreateStuffViewModel) As Task(Of ActionResult)
        If ModelState.IsValid Then
            Dim newStuff = New Stuff() With {
                .Name = model.Name,
                .Description = model.Description
            }

            Dim curUserID = User.Identity.GetUserId()
            Dim thisUser As AppUser = db.Users.Where(Function(x) x.Id = curUserID).FirstOrDefault()
            newStuff.MyUser = thisUser

            'model.ItemNames is Nothing:
            Dim thisList As List(Of String) = model.ItemNames
            If thisList IsNot Nothing Then
                Dim thisItem As Item
                For Each item In thisList
                    thisItem = (From thing In db.Items Select thing Where thing.MyUser.Id = curUserID AndAlso thing.Name = item)
                    newStuff.Items.Add(thisItem)
                Next
            End If

            db.Stuffs.Add(newStuff)
            Await db.SaveChangesAsync()
            Return RedirectToAction("Index")
        End If
        Return View(model)
    End Function

So the view works fine - the Add button adds the text to the list box as expected. But when I hit the "Create" button, in the controller, the ItemNames list is NOTHING, and I do not know why.

Any help is greatly appreciated!

0

There are 0 answers