Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add an item to a list in a ViewModel using Razor and .NET Core?

So here's my situation.

Let's say I have a view called TheView.cshtml. TheView.cshtml has a ViewModel called TheViewModel.cs. In TheViewModel.cs, resides a List of an object (TheObject) called TheObjectList.

I have an editor template for TheObject called TheObject.cshtml. Using this editor template, I can simply display all of the items in the TheObjectList with @Html.EditorFor(model => model.TheObjectList).

However, now I want to add objects to this list dynamically. I have an AJAX function, which calls a simple partial view to give the user a blank row to add a new "TheObject", however, any new TheObject I add dynamically is not considered part of the original TheObjectList.

This is because each item in the original TheObjectList is created with a certain prefix based on its index in the original list, whereas each new dynamic TheObject is created without a prefix, thus Razor does not see it as part of the list.

Is there a way around this?

TheView.cshtml

@model Models.ViewModels.TheViewModel

<table id="Table">
    <tbody>
        @Html.EditorFor(m => m.TheObjectList);
    </tbody>
</table>

<button id="AddObject" type="button" class="btn btn-primary">Add Object</button>

TheViewModel.cs

public class TheViewModel
{
    public List<TheObject> TheObjectList { get; set; }
}

AddObject Controller Method

public IActionResult AddObject()
{
   return PartialView("_EmptyRow", new TheObject());
}

AJAX Method to Add Object

$('#AddObject').click(function () {
    $.ajax({
        url: 'AddObject',
        cache: false,
        success: function (data) {
            $('#Table > tbody').append(data);
        },
        error: function (a, b, c) {
            alert(a + " " + b + " " + c);
        }
     });
});
like image 336
Daath Avatar asked Mar 30 '16 18:03

Daath


1 Answers

You basically needs to generate/return markup which looks same as what the editor template generates for your form fields except for the element index . You need to pass the index from client side which will be a part of the form field name.

Let's assume your editor template looks like below and your TheObject has a GroupName property

@model TheObject
<div>
    @Html.HiddenFor(x => x.GroupName)
    @Html.TextBoxFor(s=>s.GroupName,new {@class="thingRow"})
</div>

Now when you render your page with your current code, Editor template will generate input fields like this

<input class="thingRow" id="TheObjectList_0__GroupName" 
                           name="TheObjectList[0].GroupName" type="text" value="Something">

where 0 will be replaced with the index of the items in your TheObjectList collection.

Now let's say you already have 5 items in the collection, So when user clicks add button, you want to generate markup like above except 0 will be replaced with 5(for the sixth item). So let's update the ajax call to include the current number of items.

$('#AddObject').click(function () {
    var i = $(".thingRow").length;
    $.ajax({
        url: 'AddObject?index=' + i,
        success: function (data) {
            $('#Table > tbody').append(data);
        },
        error: function (a, b, c) {
            console.log(a, b, c);
        }
    });
});

That means, we need to accept the index value in our action method. Since we need to pass this index value from action method to our view to build the input field name value, I added a property to your class called Index

public ActionResult AddObject(int index)
{
    return PartialView("_EmptyRow", new TheObject { Index = index});
}

Now in your _EmptyRow partial view,

@model TheObject
<input id="TheObjectList_@(Model.Index)__GroupName"class="thingRow" 
             name="TheObjectList[@(Model.Index)].GroupName"  type="text" value=""/>

Now when you submit the form, model binding will work for these dynamically added items, assuming you have your Table inside a form.

@model TheViewModel
@using (Html.BeginForm())
{   
    <table id="Table">
        <tbody>
            @Html.EditorFor(m => m.TheObjectList);
        </tbody>
    </table>
    <button id="AddObject" type="button" class="btn btn-primary">Add Object</button>
    <button type="submit" class="btn btn-primary">Save</button>
}
like image 193
Shyju Avatar answered Sep 28 '22 01:09

Shyju