1. Description
I want to open the "Create" page, fill in the invoice header data, fill in the invoice dynamic line data and save everything in one submission.
(* updated code) I managed to solve the RowList error which did not allow me to add multiple rows:
How to use AddRange to insert multiple rows in an ASP.NET Core Razor page
Now I get the error
1.1 Error
ArgumentNullException: Value cannot be null. (parameter "source")
System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument parameter)
ArgumentNullException: Value cannot be null. (parameter "source")
System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) System.Linq.Enumerable.Count<TSource>(IEnumerable<TSource> source) EPIDENT5.Pages.Magazina.Pages_Magazina_Create.<ExecuteAsync>b__28_0() in Create.cshtml
...
@for (int i = 0; i < Model.RowList.Count(); i++)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder) Microsoft.AspNetCore.Mvc.TagHelpers.RenderAtEndOfFormTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output) Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.g__Awaited|0_0(task task, TagHelperExecutionContextexecutionContext, int i, int count) EPIDENT5.Pages.Magazina.Pages_Magazina_Create.ExecuteAsync()
in Create.cshtmlViewData["Title"] = "Create";
I know I'm missing something, but don't understand what the problem is.
2. Question
How do i do this?
<强>3. Front-end code:
<form method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> // This is the header table <table class="table table-striped border-0" style="width: 100%; text-align: left;"> <thead class="border-0"> <tr class="border-0"> <td class="border-0" style="min-width:100%;"> <div class="border border-secondary p-4"> <div class="row "> <div style="float:left;width:50%;"> <div class="form-group m-1"> <label asp-for="InvHeader.Cli" class="control-label" style="font-size:80%;">Client</label> <input asp-for="InvHeader.Cli" class="form-control" value="clients name" /> </div> </div> <div style="float:left;width:25%;"> <div class="form-group m-1"> <input asp-for="InvHeader.InvDate" class="form-control" type="date" value="@(DateTime.UtcNow.Date.ToString("yyyy-MM-dd"))" /> <span asp-validation-for="InvHeader.InvDate" class="text-danger"></span> </div> </div> <div style="float:left;width:25%;"> <div class="form-group m-1"> <input asp-for="InvHeader.InvNr" class="form-control" value="33" /> <span asp-validation-for="InvHeader.InvNr" class="text-danger"></span> </div> </div> </div> </div> </td> </tr> </thead> </table> //This is the dynamic rows table <table id="table1" border="0"> <tr style="font-size:80%;"> <th style="width:50%;">Product</th> <th style="width:5%;">qty</th> <th style="width:5%;">price</th> </tr> foreach (var item in Model.PL) { <tr class="border-bottom"> <td> <select id="Products" asp-for="@Model.PL[@i].ProdId" class="form-control" type="text" name="data[@i][ProdId]" style="width:100%;" > @foreach (var item in Model.PL) { <option value="@item.ProdId" qty="@qty" price="@Convert.ToInt32(item.price)">@item.name, @Convert.ToInt32(item.price)</option> } </select> </td> <td><input asp-for="@Model.MR[@i].qty" class="form-control" type="text" name="qty[@i]" style="width:100%;" /></td> <td><input asp-for="@Model.MR[@i].price" class="form-control" type="text" name="price[@i]" style="width:100%;" /></td> </tr> </table> </form>
4.Backend code
//Data binding part
[BindProperty] public InvHeader InvHeader { get; set; } = default!; public IList<InvRow> RowList { get; set; }
//onget method, where row count id red
public IActionResult OnGetAsync() { var rr = new List<InvRow>() { new InvRow() { Name = "Apple", Qty = 5, Price = 100 }, new InvRow() { Name = "Peach", Qty = 3, Price = 500 }, new InvRow() { Name = "Ananas", Qty = 1, Price = 1100 }, }; RowList = rr; return Page(); }
//Onpost method
public async Task<IActionResult> OnPostAsync(IList<InvRow> RowList) { if (!ModelState.IsValid) { return Page(); } _context.InvHeaders.Add(InvHeader); await _context.InvRows.AddRangeAsync(RowList); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
Model binding binds properties through the name attribute. The correct name property matching the parameter should look like:
addrows[index].propertyName
.Not sure what the
PL
is in the page, but it seems that only theqty
andprice
inputs are related to theInvRows model. You need to change both input names as follows:
If the select element is also related to the
InvRows
model, just change the select name, for example:addrows[@i].ProdId
. Anyway, the name depends on your model.Additionally, your page contains a duplicate foreach with the same name, which is incorrect. The hypothesis should be:
Full working demo you can follow:
model
page
Page model
Please note that the default
option
element below does not have aqty
orprice
attribute, not sure what you want to do, but I need to remind you that this Doesn't make any sense for model binding.