2
Answers

MVC Adding new entry in Master-Detail list collection via PartialView

Photo of Sirch Sarso

Sirch Sarso

Jun 02
607
1

The objective was to add new entries in the collection via PartialView. In the first entry, checking via debugging it was working fine. But if i add another entry, it doesn't retain or the last entries is not included in the collection. 
What seems to be the problem that my entries are not save/collected in the collection?
Model :

public class MasterInfo {
    public int id { get; set; }
    public string name { get; set; }
}

public class DetailInfo
{
    public int id { get; set; }
    public string data_name { get; set; }
    public int data_val { get; set; }
}

public class MasterDtlViewModel
{
    public MasterDtlViewModel() {
        this.Details = new List<DetailInfo>();
    }
    public MasterInfo Master { get; set; }
    public List<DetailInfo> Details { get; set; }
}
ASP.NET (C#)

Controller

public ActionResult NewMaster()
{
    var model = new MasterDtlViewModel();
    model.Master = new MasterInfo { id = 1, name = "Chris" };
    model.Details.Add(new DetailInfo
    {
        data_name = "Fee",
        data_val = 1000
    });
    model.Details.Add(new DetailInfo
    {
        data_name = "Charge",
        data_val = 500
    });


    return View("CreateMasterDtlView", model);
}
ASP.NET (C#)

 

//POST
public ActionResult NewMaster(MasterDtlViewModel model)
{
    var testDetailEntry = model.Details;
    return View("", model);
}

//POST
public ActionResult AddNewEntry(MasterDtlViewModel model, DetailInfo newEntry)
{
    model.Details.Add(newEntry);
    return PartialView("_tableDetailView", model);
}
ASP.NET (C#)

View :

@model Permit.MasterDtlViewModel

@{

}

<div class="col-md-12">

    @using (Html.BeginForm())
    {
        <div class="panel-group">
            <div class="panel panel-success">
                <div class="panel-heading">
                    <div class="panel-title">Master</div>
                </div>
                <div class="panel-body">
                    <div class="form-group row">
                        @Html.LabelFor(m => m.Master.id, "Customer Id", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBoxFor(m => m.Master.id, null, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group row">
                        @Html.LabelFor(m => m.Master.name, "Customer Name", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBoxFor(m => m.Master.name, null, new { @class = "form-control" })
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="panel-group">
            <div class="panel panel-success">
                <div class="panel-heading">
                    <div class="panel-title">Detail Info</div>
                </div>
                <div class="panel-body">
                    <div class="form-group row">
                        @Html.Label("DataName", "Data Name", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBox("DataName", null, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group row">
                        @Html.Label("DataValue", "Data Value", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBox("DataValue", null, new { @class = "form-control" })
                        </div>
                    </div>

                    <div class="col-md-4 col-md-offset-4">
                        <input type="button" id="AddButton" value="Add" class="btn btn-info" />
                    </div>
                </div>
            </div>
        </div>

        <div class="panel-group">
            <div class="panel panel-success">
                <div class="panel-heading">
                    <div class="panel-title">Added Details</div>
                </div>

                <div id="dtlTable">
                    @Html.Partial("_tableDetailView", Model)
                </div>
            </div>
        </div>

        <div class="col-md-4 col-md-offset-4">
            <input type="submit" value="Save" class="btn btn-success" />
        </div>
    }

</div>

@section scripts {
    <script>
        $("#AddButton").click(function () {
            var mainModel = @Html.Raw(Json.Encode(Model));

            var dataName = $("#DataName").val();
            var dataValue = $("#DataValue").val();

            var inputs = {
                "id": 0,
                "data_name": dataName,
                "data_val": dataValue
            };

            var targetUrl = '/Permit/MastDetail/AddNewEntry';

            $.ajax({
                type: "POST",
                url: targetUrl,
                contentType: "application/json",
                data: JSON.stringify({ model: mainModel, newEntry: inputs  }),
                success: function (result, status, xhr) {
                    $("#dtlTable").html(result).show();
                    $("#DataName").val("");
                    $("#DataValue").val("");
                },
                error: function (xhr, status, error) {
                    // $("#dataDiv").html("Result: " + status + " " + error + " " + xhr.status + " " + xhr.statusText)
                }
            });


        });
    </script>
}
ASP.NET (C#)

PartialView

@model Permit.MasterDtlViewModel


<div class="panel-body">
    <table class="table table-bordered table-hover table-striped table-responsive">
        <thead>
            <tr>
                <td>Idx</td>
                <td>Data Name</td>
                <td>Value</td>
            </tr>
        </thead>
        <tbody>
            @if (Model.Details != null)
            {
                for (int i = 0; i < Model.Details.Count; i++)
                {
                    <tr>
                        <td><input type="hidden" ("Details[{0}].id",i)" />  @Model.Details[i].id </td>
                        <td><input type="hidden" ("Details[{0}].data_name",i)" />@Model.Details[i].data_name </td>
                        <td><input type="hidden" ("Details[{0}].data_val",i)" />@Model.Details[i].data_val </td>
                    </tr>
                }
            }
        </tbody>
    </table>
</div>
ASP.NET (C#)

(Note: Having difficulty attaching the codes)

Answers (2)

2
Photo of Mayooran Navamany
265 6.8k 179.4k Jun 03

The issue you're encountering with not retaining entries in the collection when adding new entries via PartialView in an MVC application often stems from the way the model state is managed during the AJAX requests. When you update the view with a PartialView, the model state is not preserved between requests, causing the entries to be lost.

Here's a more detailed solution to ensure that the entries are retained:

  1. Store the Current State in a Hidden Field: Store the current state of the Details list in a hidden field so it can be included in the model during subsequent requests.

  2. Update the JavaScript to Handle the Hidden Field: Modify the JavaScript to read and update this hidden field.

  3. Update the Controller to Use the Updated List: Ensure the controller merges the new entries with the existing entries.

Here's how you can implement these changes:

Step 1: Update the View

Add a hidden field to store the current state of the Details list:

@model Permit.MasterDtlViewModel

<div class="col-md-12">
    @using (Html.BeginForm())
    {
        @Html.HiddenFor(m => m.Details)

        <div class="panel-group">
            <!-- Master Info Section -->
            <div class="panel panel-success">
                <div class="panel-heading">
                    <div class="panel-title">Master</div>
                </div>
                <div class="panel-body">
                    <div class="form-group row">
                        @Html.LabelFor(m => m.Master.id, "Customer Id", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBoxFor(m => m.Master.id, null, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group row">
                        @Html.LabelFor(m => m.Master.name, "Customer Name", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBoxFor(m => m.Master.name, null, new { @class = "form-control" })
                        </div>
                    </div>
                </div>
            </div>
            <!-- Detail Info Section -->
            <div class="panel panel-success">
                <div class="panel-heading">
                    <div class="panel-title">Detail Info</div>
                </div>
                <div class="panel-body">
                    <div class="form-group row">
                        @Html.Label("DataName", "Data Name", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBox("DataName", null, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group row">
                        @Html.Label("DataValue", "Data Value", new { @class = "control-label col-md-4" })
                        <div class="col-md-4">
                            @Html.TextBox("DataValue", null, new { @class = "form-control" })
                        </div>
                    </div>

                    <div class="col-md-4 col-md-offset-4">
                        <input type="button" id="AddButton" value="Add" class="btn btn-info" />
                    </div>
                </div>
            </div>
        </div>
        <!-- Table to display details -->
        <div class="panel-group">
            <div class="panel panel-success">
                <div class="panel-heading">
                    <div class="panel-title">Added Details</div>
                </div>
                <div id="dtlTable">
                    @Html.Partial("_tableDetailView", Model)
                </div>
            </div>
        </div>
        <!-- Submit button -->
        <div class="col-md-4 col-md-offset-4">
            <input type="submit" value="Save" class="btn btn-success" />
        </div>
    }
</div>

@section scripts {
    <script>
        $("#AddButton").click(function () {
            var mainModel = @Html.Raw(Json.Encode(Model));

            var dataName = $("#DataName").val();
            var dataValue = $("#DataValue").val();

            var newEntry = {
                id: 0,
                data_name: dataName,
                data_val: dataValue
            };

            // Append new entry to hidden field data
            var currentDetails = $("#Details").val();
            var detailsArray = currentDetails ? JSON.parse(currentDetails) : [];
            detailsArray.push(newEntry);
            $("#Details").val(JSON.stringify(detailsArray));

            $.ajax({
                type: "POST",
                url: '@Url.Action("AddNewEntry", "YourControllerName")',
                contentType: "application/json",
                data: JSON.stringify({ model: mainModel, newEntry: newEntry }),
                success: function (result, status, xhr) {
                    $("#dtlTable").html(result).show();
                    $("#DataName").val("");
                    $("#DataValue").val("");
                },
                error: function (xhr, status, error) {
                    // Handle error
                }
            });
        });
    </script>
}

Step 2: Update the Controller

In the AddNewEntry action, merge the new entry with the existing entries from the hidden field:

// POST
public ActionResult AddNewEntry(MasterDtlViewModel model, DetailInfo newEntry)
{
    if (model.Details == null)
    {
        model.Details = new List<DetailInfo>();
    }

    model.Details.Add(newEntry);

    return PartialView("_tableDetailView", model);
}

Step 3: Ensure Partial View is Correct

Make sure the partial view correctly displays the details:

@model Permit.MasterDtlViewModel

<div class="panel-body">
    <table class="table table-bordered table-hover table-striped table-responsive">
        <thead>
            <tr>
                <td>Idx</td>
                <td>Data Name</td>
                <td>Value</td>
            </tr>
        </thead>
        <tbody>
            @if (Model.Details != null)
            {
                for (int i = 0; i < Model.Details.Count; i++)
                {
                    <tr>
                        <td>@Html.HiddenFor(m => m.Details[i].id) @Model.Details[i].id</td>
                        <td>@Html.HiddenFor(m => m.Details[i].data_name) @Model.Details[i].data_name</td>
                        <td>@Html.HiddenFor(m => m.Details[i].data_val) @Model.Details[i].data_val</td>
                    </tr>
                }
            }
        </tbody>
    </table>
</div>

Explanation

  • Hidden Field for Details: This field ensures that the current state of the details list is persisted across requests.
  • JavaScript Handling: The AddButton click event handler reads the current state from the hidden field, appends the new entry, and updates the hidden field before making the AJAX request.
  • Controller Update: The controller merges the new entry with the existing details and returns the updated partial view.

This approach ensures that each new entry is added to the collection, and the updated list is displayed correctly in the view.

1
Photo of Sirch Sarso
NA 193 1.7k Jun 05

Sir Navamany.
I tried your suggestion but i received an error "Uncaught SyntaxError: Unexpected token 'S', "System.Col"... is not valid JSON".
I console the actual value of hiddenfield details and it is System.Collections.Generic.List`1[Permit.DetailInfo] and not referring to the actual list values.