skip to Main Content

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


  1. Chosen as BEST ANSWER

    This what worked for me:

    1. This is the frontend code

     <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    
           
            <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">
                                            <label asp-for="InvHeader.Cli" class="control-label" style="font-size:80%;">Date</label>
                                            <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">
                                            <label asp-for="InvHeader.Cli" class="control-label" style="font-size:80%;">Nr</label>
                                            <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>
    
            <table id="table1" border="0">
                <tr style="font-size:80%;">
                    <th style="width:50%;">Name</th>
                    <th style="width:5%;">qty</th>
                    <th style="width:5%;">price</th>
                </tr>
    
                @for (int i = 0; i < Model.RowList.Count(); i++)
                {
    
                    <tr class="border-bottom">
                        <td>
                            <div class="form-group">
                                  <input asp-for="@Model.RowList[@i].Name" class="form-control" name="RowList[@i].Name"  />
                                <span asp-validation-for="@Model.RowList[@i].Name" class="text-danger"></span>
                            </div>
                        </td>
                        <td>
                            <div class="form-group">
                                <input asp-for="@Model.RowList[@i].Qty" class="form-control" name="RowList[@i].Qty" value="1" />
                                <span asp-validation-for="@Model.RowList[@i].Qty" class="text-danger"></span>
                            </div>
                        </td>
                        <td>
                            <div class="form-group">
                                <input asp-for="@Model.RowList[@i].Price" class="form-control" name="RowList[@i].Price" value="33" />
                                <span asp-validation-for="@Model.RowList[@i].Price" class="text-danger"></span>
                            </div>
                        </td>
                    </tr>
                }
            </table>
           
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
                <a asp-page="Index">Back to List</a>
            </div>
        </form>
    

    2. This is the backend code

     [BindProperty]
        public InvHeader InvHeader { get; set; } = default!;
        public IList<InvRow> RowList { get; set; }
    
    
        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();
        }
    
    
    
        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        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. 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 only qty and price input are related to the InvRows model. You need change the two inputs name like below:

    <td><input asp-for="@Model.MR[@i].qty" class="form-control" type="text" name="addrows[@i].qty" style="width:100%;" /></td>
    <td><input asp-for="@Model.MR[@i].price" class="form-control" type="text" name="addrows[@i].price" style="width:100%;" /></td>
    

    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:

    @for(int i = 0;i<Model.PL.Count();i++)
    {
            
    <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">@item.name,   @Convert.ToInt32(item.price)</option>
                }
            </select>
        </td>
        <td><input asp-for="@Model.MR[@i].qty" class="form-control" type="text" name="addrows[@i].qty" style="width:100%;" /></td>
        <td><input asp-for="@Model.MR[@i].price" class="form-control" type="text" name="addrows[@i].price" style="width:100%;" /></td>
    </tr>
    }
    

    A whole working demo you could follow:

    Model

    public class InvHeaders
    {
        public int CliId { get; set; }
        public DateTime InvDate { get; set; }
        public string InvNr { get; set; }
    }
    public class InvRows 
    {
        public int qty { get; set; }
        public int price { get; set; }
    }
    public class Product
    {
        public int ProdId { get; set; }
        public string name { get; set; }
        public int price { get; set; }
    }
    

    Page

    @page
    @model IndexModel
    
    <form method="post">
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <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:25%;">
                                    <div class="form-group m-1">
                                        <label asp-for="InvHeader.CliId" class="control-label" style="font-size:80%;">Client</label>
                                        <select asp-for="InvHeader.CliId" class="form-control font-weight-bold text-primary" asp-items="ViewBag.FurId"></select>
                                    </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="" />
                                        <span asp-validation-for="InvHeader.InvNr" class="text-danger"></span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
            </thead>
        </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>
           
            @for(int i = 0;i<Model.PL.Count();i++)
            {
                
            <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">@item.name,   @Convert.ToInt32(item.price)</option>
                        }
                    </select>
                </td>
                <td><input asp-for="@Model.MR[@i].qty" class="form-control" type="text" name="addrows[@i].qty" style="width:100%;" /></td>
                <td><input asp-for="@Model.MR[@i].price" class="form-control" type="text" name="addrows[@i].price" style="width:100%;" /></td>
            </tr>
            }
        </table>
        <input type="submit" value="POST" />
    </form>
    

    PageModel

    public class IndexModel : PageModel
    {
        [BindProperty]
        public InvHeaders InvHeader { get; set; } = default!;
        [BindProperty]
        public InvRows InvRow { get; set; } = default!;
        public IList<InvRows> MR { get; set; } = default!;
        public List<Product> PL { get; set; }
    
        public void OnGet()
        {   
            //hard-coded the value is just for easy testing
            PL = new List<Product>()
            {
                new Product(){ProdId=1,name="aa",price=12},
                new Product(){ProdId=2,name="bb",price=16},
                new Product(){ProdId=3,name="cc",price=21}
            };
        }
        public void OnPost(List<InvRows> addrows)
        {
             //do your stuff....
        }
    }
    

    Just a note, there is no qty or price attribute in default option element below, not sure what do you want to do but I need remind you that it makes no sense for model binding.

    <option value="@item.ProdId" qty="@qty" price="@Convert.ToInt32(item. Price)">
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search