1.Explaination
I want to open the "Create" page, fill the invoice header data, fill the invoice dynamic rows data and save everything on one single submit.
(* Updated code)
I managed to solve the RowList error, which did not let me add multiple rows:
How to insert multiple rows with AddRange in ASP.NET Core Razor pages
Now i am getting an error
1.1 The error
ArgumentNullException: Value cannot be null. (Parameter ‘source’)
System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
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, TagHelperExecutionContext executionContext, int i, int count)
EPIDENT5.Pages.Magazina.Pages_Magazina_Create.ExecuteAsync() in Create.cshtml
ViewData["Title"] = "Create";
I know i am missing something but can not understand where the problem is.
2.Question
How can this be done?
3. The frontend 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.The backend code
//The data binding part
[BindProperty]
public InvHeader InvHeader { get; set; } = default!;
public IList<InvRow> RowList { get; set; }
//The 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();
}
//The 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");
}
2
Answers
This what worked for me:
1. This is the frontend code
2. This is the backend code
Model Binding binds the property by name attribute. The correct name attribute which matches the parameter should be like:
addrows[index].propertyName
.Not sure what is your
PL
in your page, but it seems onlyqty
andprice
input are related to theInvRows
model. You need change the two inputs name like below:If the select element is also related to the
InvRows
model, just change the select name like:addrows[@i].ProdId
. Any way, the name depends on how is your model like.Besides, your page contains duplicated foreach with same name, it is not correct. Assume that is should be:
A whole working demo you could follow:
Model
Page
PageModel
Just a note, there is no
qty
orprice
attribute in defaultoption
element below, not sure what do you want to do but I need remind you that it makes no sense for model binding.