You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Razor Page中实现表单数据添加至绑定DataTable的服务器端列表并同步更新表格的技术问询

Solution to Add Rows to Grid and Save to Database in Razor Pages

Let's break down the implementation step-by-step to achieve your desired functionality:

1. Client-Side Updates (cshtml)

First, we'll modify the front-end to handle dynamic row addition, auto-calculate totals, and ensure form data binds correctly to your server-side list.

Updated cshtml Code

@{ ViewData["Title"] = "Create"; Layout = "~/Pages/Shared/_Layout.cshtml"; }
<h1>Create</h1>
<h4>Trans_Detail</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label class="control-label">Vendor</label>
                <select asp-for="VendorGUIDBind" class ="form-control" asp-items="ViewBag.Vendor_GUID" >
                </select>
            </div>
            <div class="form-group">
                <label asp-for="tbl_AC_Trans_Detail.Cement_GUID" class="control-label">Cement</label>
                <select asp-for="tbl_AC_Trans_Detail.Cement_GUID" class ="form-control" asp-items="ViewBag.Cement_GUID" id="ddlCement">
                    <option value="">Select Cement</option>
                </select>
            </div>
            <div class="form-group">
                <label asp-for="tbl_AC_Trans_Detail.Cement_Type_GUID" class="control-label">Cement Type</label>
                <select asp-for="tbl_AC_Trans_Detail.Cement_Type_GUID" class ="form-control" asp-items="ViewBag.Cement_Type_GUID" id="ddlCementType">
                    <option value="">Select Cement Type</option>
                </select>
            </div>
            <div class="form-group">
                <label asp-for="tbl_AC_Trans_Detail.Cement_Quantity" class="control-label">Cement Quantity</label>
                <input asp-for="tbl_AC_Trans_Detail.Cement_Quantity" class="form-control" id="txtCementQuantity"/>
                <span asp-validation-for="tbl_AC_Trans_Detail.Cement_Quantity" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="tbl_AC_Trans_Detail.Cement_Price" class="control-label">Cement Price</label>
                <input asp-for="tbl_AC_Trans_Detail.Cement_Price" class="form-control" id="txtCementPrice" />
                <span asp-validation-for="tbl_AC_Trans_Detail.Cement_Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="tbl_AC_Trans_Detail.Total_Amount" class="control-label">Total Amount</label>
                <input asp-for="tbl_AC_Trans_Detail.Total_Amount" class="form-control" id="txtTotalAmount" readonly />
                <span asp-validation-for="tbl_AC_Trans_Detail.Total_Amount" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
                <input type="button" value="Add Row" class="btn btn-secondary" onclick="AddProduct()" />
            </div>
        </form>
    </div>
</div>
<table id="example2" class="display" cellspacing="0" width="100%">
    <thead>
        <tr>
            <th>Cement</th>
            <th>Cement Type</th>
            <th>Quantity</th>
            <th>Cement Price</th>
            <th>Total</th>
        </tr>
    </thead>
    <tbody>
        @for (int i = 0; i < Model.gridBind.Count; i++)
        {
            <tr>
                <td>@Html.DisplayFor(modelItem => Model.gridBind[i].Cement_GU.Cement_Name)</td>
                <td>@Html.DisplayFor(modelItem => Model.gridBind[i].Cement_Type_GU.Cement_Type_Name)</td>
                <td>@Html.DisplayFor(modelItem => Model.gridBind[i].Cement_Quantity)</td>
                <td>@Html.DisplayFor(modelItem => Model.gridBind[i].Cement_Price)</td>
                <td>@Html.DisplayFor(modelItem => Model.gridBind[i].Total_Amount)</td>
                <!-- Hidden inputs for model binding -->
                <td style="display:none;">
                    @Html.HiddenFor(modelItem => Model.gridBind[i].AC_Trans_Detail_GUID)
                    @Html.HiddenFor(modelItem => Model.gridBind[i].Cement_GUID)
                    @Html.HiddenFor(modelItem => Model.gridBind[i].Cement_Type_GUID)
                    @Html.HiddenFor(modelItem => Model.gridBind[i].Cement_Quantity)
                    @Html.HiddenFor(modelItem => Model.gridBind[i].Cement_Price)
                    @Html.HiddenFor(modelItem => Model.gridBind[i].Total_Amount)
                </td>
            </tr>
        }
    </tbody>
</table>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
    <link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet" />
    <script>
        $(document).ready(function() {
            // Initialize DataTable
            $('#example2').DataTable();

            // Auto-calculate total amount when quantity/price changes
            $('#txtCementQuantity, #txtCementPrice').on('input', function() {
                var quantity = parseFloat($('#txtCementQuantity').val()) || 0;
                var price = parseFloat($('#txtCementPrice').val()) || 0;
                $('#txtTotalAmount').val((quantity * price).toFixed(2));
            });

            // Populate Cement Type dropdown when Cement is selected
            $('#ddlCement').change(function() {
                var cementGUID = $(this).val();
                if (cementGUID) {
                    $.get(`?handler=CementTypes&id=${cementGUID}`, function(data) {
                        $('#ddlCementType').empty();
                        $('#ddlCementType').append('<option value="">Select Cement Type</option>');
                        $.each(data, function(index, item) {
                            $('#ddlCementType').append(`<option value="${item.Cement_Type_GUID}">${item.Cement_Type_Name}</option>`);
                        });
                    });
                } else {
                    $('#ddlCementType').empty();
                    $('#ddlCementType').append('<option value="">Select Cement Type</option>');
                }
            });
        });

        function AddProduct() {
            // Get form values
            var cementGUID = $('#ddlCement').val();
            var cementTypeGUID = $('#ddlCementType').val();
            var cementQuantity = $('#txtCementQuantity').val();
            var cementPrice = $('#txtCementPrice').val();
            var totalAmount = $('#txtTotalAmount').val();

            // Get display names for the table
            var cementName = $('#ddlCement option:selected').text();
            var cementTypeName = $('#ddlCementType option:selected').text();

            // Validate required fields
            if (!cementGUID || !cementTypeGUID || !cementQuantity || !cementPrice) {
                alert('Please fill all required fields (Cement, Cement Type, Quantity, Price)!');
                return;
            }

            // Ensure numeric values are valid
            if (isNaN(parseFloat(cementQuantity)) || isNaN(parseFloat(cementPrice))) {
                alert('Quantity and Price must be valid numbers!');
                return;
            }

            // Add row to DataTable
            var table = $('#example2').DataTable();
            var nextIndex = table.rows().count();

            table.row.add([
                cementName,
                cementTypeName,
                cementQuantity,
                cementPrice,
                totalAmount
            ]).draw();

            // Append hidden inputs to form for model binding
            var form = $('form');
            var newGuid = generateGUID();

            form.append(`<input type="hidden" name="gridBind[${nextIndex}].AC_Trans_Detail_GUID" value="${newGuid}" />`);
            form.append(`<input type="hidden" name="gridBind[${nextIndex}].Cement_GUID" value="${cementGUID}" />`);
            form.append(`<input type="hidden" name="gridBind[${nextIndex}].Cement_Type_GUID" value="${cementTypeGUID}" />`);
            form.append(`<input type="hidden" name="gridBind[${nextIndex}].Cement_Quantity" value="${cementQuantity}" />`);
            form.append(`<input type="hidden" name="gridBind[${nextIndex}].Cement_Price" value="${cementPrice}" />`);
            form.append(`<input type="hidden" name="gridBind[${nextIndex}].Total_Amount" value="${totalAmount}" />`);

            // Clear form fields
            $('#ddlCement').val('').trigger('change');
            $('#txtCementQuantity').val('');
            $('#txtCementPrice').val('');
            $('#txtTotalAmount').val('');
        }

        // Generate a valid GUID for new detail rows
        function generateGUID() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }
    </script>
}

2. Server-Side Updates (cshtml.cs)

Next, we'll modify the backend to handle form submission, save the main transaction, and all associated details.

Updated cshtml.cs Code

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ArabianCement.Pages
{
    public class CreateModel : PageModel
    {
        private readonly ArabianCement.Models.ArabianCementContext _context;
        private readonly IConfiguration _configuration;

        public CreateModel(ArabianCement.Models.ArabianCementContext context, IConfiguration configuration)
        {
            _context = context;
            _configuration = configuration;
        }

        [BindProperty]
        public Guid VendorGUIDBind { get; set; }

        [BindProperty]
        public List<tbl_AC_Trans_Detail> gridBind { get; set; } = new List<tbl_AC_Trans_Detail>();

        [BindProperty]
        public tbl_AC_Trans_Detail tbl_AC_Trans_Detail { get; set; }

        public IActionResult OnGet()
        {
            ViewData["Cement_GUID"] = new SelectList(_context.LK_Cements, "Cement_GUID", "Cement_Name");
            ViewData["Vendor_GUID"] = new SelectList(_context.LK_Sources, "Source_GUID", "Source_Name");
            return Page();
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                // Repopulate dropdowns if validation fails
                ViewData["Cement_GUID"] = new SelectList(_context.LK_Cements, "Cement_GUID", "Cement_Name", tbl_AC_Trans_Detail.Cement_GUID);
                ViewData["Vendor_GUID"] = new SelectList(_context.LK_Sources, "Source_GUID", "Source_Name", VendorGUIDBind);
                return Page();
            }

            // Create main transaction record
            var mainTransaction = new tbl_AC_Tran
            {
                AC_Trans_GUID = Guid.NewGuid(),
                Creation_Date = DateTime.Now,
                Vendor_GUID = VendorGUIDBind,
                Trans_Type = Guid.Parse(_configuration["AppKeys:SellGUID"])
            };

            _context.tbl_AC_Trans.Add(mainTransaction);
            await _context.SaveChangesAsync();

            // Add all detail records linked to the main transaction
            foreach (var detail in gridBind)
            {
                detail.AC_Trans_GUID = mainTransaction.AC_Trans_GUID;
                _context.tbl_AC_Trans_Details.Add(detail);
            }

            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }

        public JsonResult OnGetCementTypes(string id)
        {
            if (!Guid.TryParse(id, out Guid cementGUID))
            {
                return new JsonResult(new List<LK_Cement_Type>());
            }

            var cementTypes = _context.LK_Cement_Types
                .Where(x => x.Cement_GUID == cementGUID)
                .Select(x => new { x.Cement_Type_GUID, x.Cement_Type_Name })
                .ToList();

            return new JsonResult(cementTypes);
        }
    }
}

Key Features Explained

  1. Dynamic Row Addition:

    • The AddProduct() function captures form values, validates them, adds a row to the DataTable, and appends hidden inputs to ensure the new row is bound to the gridBind list on submission.
  2. Auto-Calculated Totals:

    • JavaScript automatically computes the total amount when quantity or price is changed, and the total input is set to readonly to prevent manual edits.
  3. Model Binding:

    • We use a for loop instead of foreach to render table rows, which generates correctly indexed hidden inputs for model binding to the gridBind list.
  4. Database Saving:

    • The OnPostAsync method first saves the main transaction, then links all details from gridBind to the main transaction and saves them to the database.
  5. Dropdown Dependencies:

    • The Cement Type dropdown is populated dynamically when a Cement is selected using AJAX to call the OnGetCementTypes handler.

内容的提问来源于stack exchange,提问作者Allam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 11:12:31