asp.net core - Razor Pages Binding a List<t> to HTML Table - Stack Overflow

I am using ASP.NET Core 8.0 with Razor Pages (not MVC). Webpage has a table where the prices of service

I am using ASP.NET Core 8.0 with Razor Pages (not MVC). Webpage has a table where the prices of services offered can be modified as shown here:

I have created a model class ServProdModel and a bound property collection called Servs:

[BindProperty]
public List<ServProdModel>? Servs { get; set; }

The values get bound properly, as can be seen. But I am facing issues in the POST handler.

I have bound it into the HTML Table as follows (using explicit index) like this:

@foreach (var itm in Model.Servs)
{
    <tr>
        <td class="visually-hidden">
            <input type="hidden" name='Servs.Index' value='@itm.ServID' />
            <input type="hidden" name='Servs[@itm.ServID].ServID' value='@itm.ServID' />
        </td>
        <td>
            <input type="text" name='Servs[@itm.ServID].Price' value='@itm.Price' />
        </td>
        ...
    </tr>
}

I get the collection in the Request.Form as shown below:

I also see the Servs.Count = 5, which is correct since there are 5 services.

HOWEVER, all the items in the Servs List are null. And I get an error:

System.NullReferenceException: Object reference not set to an instance of an object.

What is missing? Please help!

I also tried using for loop but the same thing happens.

[EDIT]

Image shows Collection with null objects:

Following image shows Request.Form collection after using Sequential Index instead of Explicit Index:

[UPDATE]: As promised, attaching a minimal code to reproduce the defect.

1. TestPg.cshtml

@page
@model NewCOS.Pages.Admins.TestPgModel
@{
}

@if (Model.Servs != null)
{
    <form method="post">
        <div class="row g-2 mb-1">
            <div class="col-md-8">
                <button type="submit" asp-page-handler="AddServs" class="btn btn-sm btn-success">Add Selected Services To Cart</button>
            </div>
            <div class="col-md-4">
            </div>
        </div>
        <div class="table-responsive scrollbar" style="max-height:300px">
            <table id="tblItems" class="table table-bordered table-striped fs-10">
                <thead class="clsDummy">
                    <tr>
                        <th class="visually-hidden"></th>
                        <th class="bg-200 text-center">Select</th>
                        <th class="bg-200 text-center">Services</th>
                        <th class="bg-200 text-center">Quantity</th>
                        <th class="bg-200 text-center">Price</th>
                        <th class="bg-200 text-center cos-pre">Promotion</th>
                        <th class="bg-200 text-center">Discount</th>
                        <th class="bg-200 text-center">Discount<br />Amount</th>
                    </tr>
                </thead>
                <tbody class="clsDummyBody">
                    @{
                        //int i = 0;
                        @foreach (var itm in Model.Servs)
                        {
                            <tr>
                                <td class="visually-hidden">
                                    <input type="hidden" name='Servs.Index' value='@itm.ServID' />
                                    <input type="hidden" name='Servs[@itm.ServID].ServID' value='@itm.ServID' />
                                </td>
                                <td class="text-center">
                                    <input type="checkbox" id='@("S" + itm.ProdID)' class="form-check-input" onchange="changeQty(this);" />
                                </td>
                                <td><span>@itm.Service</span></td>
                                <td>
                                    <div class="input-group input-group-sm flex-nowrap" data-quantity="data-quantity">
                                        <button class="btn btn-sm btn-outline-secondary border-300 px-2 shadow-none" data-type="minus">-</button>
                                        <input type="number" id='@("QS" + itm.ProdID)' name='Servs[@itm.ServID].Qty' value='@itm.Qty' min="0" aria-label="Quantity" style="width: 50px" class="form-control text-center px-2 input-spin-none" />
                                        <button class="btn btn-sm btn-outline-secondary border-300 px-2 shadow-none" data-type="plus">+</button>
                                    </div>
                                </td>
                                <td>
                                    <div class="d-flex flex-row">
                                        <div class="d-inline-flex pt-1">$&nbsp;</div>
                                        <div class="d-inline-flex">
                                            <input type="text" name='Servs[@itm.ServID].Price' value='@itm.Price' class="form-control form-control-sm" placeholder="Enter Price" maxlength="10" />
                                        </div>
                                    </div>
                                </td>
                                <td>
                                    <select class="form-select form-select-sm" name='Servs[@itm.ServID].PromoID' asp-for="@itm.PromoID">
                                        <option value="0">-- Select --</option>
                                        <option value="1">Senior Discount</option>
                                        <option value="2">Complementary</option>
                                        <option value="3">Student Discount</option>
                                    </select>
                                </td>
                                <td>
                                    <input class="form-control form-control-sm text-center px-2 input-spin-none" type="number" min="0" max="1" step="0.01" value="0" aria-label="Discount between 0 and 1" style="width: 50px" />
                                </td>
                                <td>$&nbsp;<span>0.00</span></td>
                            </tr>
                            //i++;
                        }
                    }
                </tbody>
            </table>
        </div>
    </form>
}

2. TestPg.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace NewCOS.Pages.Admins
{
    public class TestPgModel : PageModel
    {
        [BindProperty]
        public List<ServModel>? Servs { get; set; }

        public void OnGet()
        {
            ServModel ServProd = new ServModel();
            Servs = ServProd.Fetch(ServModel.ItemType.Service);
        }
        public void OnPostAddServs()
        {
            var chk = Request.Form;

            if (Servs != null)
            {
                foreach (var itm in Servs)
                {
                    int Qty = itm.Qty;
                    decimal prc = itm.Price;
                }
            }
        }

    }
    public class ServModel
    {
        private int _uid;
        public int SchoolUID;
        public int CompID;
        public string? UPC;
        public string ProductName = "";
        public string Service = "";
        public decimal RetailPrice = 0.00M;
        public decimal Price = 0.00M;
        public int Qty = 0;
        public int PromoID = 1;
        public decimal DiscAmount = 0.00M;
        public decimal Discount = 0.00M;
        public decimal TaxAmount = 0.00M;
        public decimal TotalAmount = 0.00M;
        public ItemType TransCode;
        public int ChkServ;
        public int ChkProd;

        public int ProdID { get { return _uid; } }
        public int ServID { get { return _uid; } }

        public enum ItemType
        {
            Product, Service
        }

        public List<ServModel>? Fetch(ItemType type)
        {
            List<ServModel>? Services = new List<ServModel>();

            if (type == ItemType.Service)
            {
                Services.Add(new ServModel { _uid = 1, TransCode = ItemType.Service, Service = "Acrylic/Gel Tips/Overlay", Price = 20.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 2, TransCode = ItemType.Service, Service = "Acrylic/Natural Nails", Price = 25.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 10, TransCode = ItemType.Service, Service = "Additional Colors Each", Price = 15.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 11, TransCode = ItemType.Service, Service = "Airstyle w/brush", Price = 5.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 17, TransCode = ItemType.Service, Service = "Beard Trim", Price = 3.00M, CompID = 1 });

                return Services;
            }
            else { return null; }
        }
    }
}

IMP: This code implements explicit index. If you would like to test with Sequential Index then

  1. Uncomment following two lines:
int i = 0;
i++;
  1. Comment this line:
<input type="hidden" name='Servs.Index' value='@itm.ServID' />
  1. Replace Servs[@itm.ServID] with Servs[@i]

I am using ASP.NET Core 8.0 with Razor Pages (not MVC). Webpage has a table where the prices of services offered can be modified as shown here:

I have created a model class ServProdModel and a bound property collection called Servs:

[BindProperty]
public List<ServProdModel>? Servs { get; set; }

The values get bound properly, as can be seen. But I am facing issues in the POST handler.

I have bound it into the HTML Table as follows (using explicit index) like this:

@foreach (var itm in Model.Servs)
{
    <tr>
        <td class="visually-hidden">
            <input type="hidden" name='Servs.Index' value='@itm.ServID' />
            <input type="hidden" name='Servs[@itm.ServID].ServID' value='@itm.ServID' />
        </td>
        <td>
            <input type="text" name='Servs[@itm.ServID].Price' value='@itm.Price' />
        </td>
        ...
    </tr>
}

I get the collection in the Request.Form as shown below:

I also see the Servs.Count = 5, which is correct since there are 5 services.

HOWEVER, all the items in the Servs List are null. And I get an error:

System.NullReferenceException: Object reference not set to an instance of an object.

What is missing? Please help!

I also tried using for loop but the same thing happens.

[EDIT]

Image shows Collection with null objects:

Following image shows Request.Form collection after using Sequential Index instead of Explicit Index:

[UPDATE]: As promised, attaching a minimal code to reproduce the defect.

1. TestPg.cshtml

@page
@model NewCOS.Pages.Admins.TestPgModel
@{
}

@if (Model.Servs != null)
{
    <form method="post">
        <div class="row g-2 mb-1">
            <div class="col-md-8">
                <button type="submit" asp-page-handler="AddServs" class="btn btn-sm btn-success">Add Selected Services To Cart</button>
            </div>
            <div class="col-md-4">
            </div>
        </div>
        <div class="table-responsive scrollbar" style="max-height:300px">
            <table id="tblItems" class="table table-bordered table-striped fs-10">
                <thead class="clsDummy">
                    <tr>
                        <th class="visually-hidden"></th>
                        <th class="bg-200 text-center">Select</th>
                        <th class="bg-200 text-center">Services</th>
                        <th class="bg-200 text-center">Quantity</th>
                        <th class="bg-200 text-center">Price</th>
                        <th class="bg-200 text-center cos-pre">Promotion</th>
                        <th class="bg-200 text-center">Discount</th>
                        <th class="bg-200 text-center">Discount<br />Amount</th>
                    </tr>
                </thead>
                <tbody class="clsDummyBody">
                    @{
                        //int i = 0;
                        @foreach (var itm in Model.Servs)
                        {
                            <tr>
                                <td class="visually-hidden">
                                    <input type="hidden" name='Servs.Index' value='@itm.ServID' />
                                    <input type="hidden" name='Servs[@itm.ServID].ServID' value='@itm.ServID' />
                                </td>
                                <td class="text-center">
                                    <input type="checkbox" id='@("S" + itm.ProdID)' class="form-check-input" onchange="changeQty(this);" />
                                </td>
                                <td><span>@itm.Service</span></td>
                                <td>
                                    <div class="input-group input-group-sm flex-nowrap" data-quantity="data-quantity">
                                        <button class="btn btn-sm btn-outline-secondary border-300 px-2 shadow-none" data-type="minus">-</button>
                                        <input type="number" id='@("QS" + itm.ProdID)' name='Servs[@itm.ServID].Qty' value='@itm.Qty' min="0" aria-label="Quantity" style="width: 50px" class="form-control text-center px-2 input-spin-none" />
                                        <button class="btn btn-sm btn-outline-secondary border-300 px-2 shadow-none" data-type="plus">+</button>
                                    </div>
                                </td>
                                <td>
                                    <div class="d-flex flex-row">
                                        <div class="d-inline-flex pt-1">$&nbsp;</div>
                                        <div class="d-inline-flex">
                                            <input type="text" name='Servs[@itm.ServID].Price' value='@itm.Price' class="form-control form-control-sm" placeholder="Enter Price" maxlength="10" />
                                        </div>
                                    </div>
                                </td>
                                <td>
                                    <select class="form-select form-select-sm" name='Servs[@itm.ServID].PromoID' asp-for="@itm.PromoID">
                                        <option value="0">-- Select --</option>
                                        <option value="1">Senior Discount</option>
                                        <option value="2">Complementary</option>
                                        <option value="3">Student Discount</option>
                                    </select>
                                </td>
                                <td>
                                    <input class="form-control form-control-sm text-center px-2 input-spin-none" type="number" min="0" max="1" step="0.01" value="0" aria-label="Discount between 0 and 1" style="width: 50px" />
                                </td>
                                <td>$&nbsp;<span>0.00</span></td>
                            </tr>
                            //i++;
                        }
                    }
                </tbody>
            </table>
        </div>
    </form>
}

2. TestPg.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace NewCOS.Pages.Admins
{
    public class TestPgModel : PageModel
    {
        [BindProperty]
        public List<ServModel>? Servs { get; set; }

        public void OnGet()
        {
            ServModel ServProd = new ServModel();
            Servs = ServProd.Fetch(ServModel.ItemType.Service);
        }
        public void OnPostAddServs()
        {
            var chk = Request.Form;

            if (Servs != null)
            {
                foreach (var itm in Servs)
                {
                    int Qty = itm.Qty;
                    decimal prc = itm.Price;
                }
            }
        }

    }
    public class ServModel
    {
        private int _uid;
        public int SchoolUID;
        public int CompID;
        public string? UPC;
        public string ProductName = "";
        public string Service = "";
        public decimal RetailPrice = 0.00M;
        public decimal Price = 0.00M;
        public int Qty = 0;
        public int PromoID = 1;
        public decimal DiscAmount = 0.00M;
        public decimal Discount = 0.00M;
        public decimal TaxAmount = 0.00M;
        public decimal TotalAmount = 0.00M;
        public ItemType TransCode;
        public int ChkServ;
        public int ChkProd;

        public int ProdID { get { return _uid; } }
        public int ServID { get { return _uid; } }

        public enum ItemType
        {
            Product, Service
        }

        public List<ServModel>? Fetch(ItemType type)
        {
            List<ServModel>? Services = new List<ServModel>();

            if (type == ItemType.Service)
            {
                Services.Add(new ServModel { _uid = 1, TransCode = ItemType.Service, Service = "Acrylic/Gel Tips/Overlay", Price = 20.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 2, TransCode = ItemType.Service, Service = "Acrylic/Natural Nails", Price = 25.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 10, TransCode = ItemType.Service, Service = "Additional Colors Each", Price = 15.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 11, TransCode = ItemType.Service, Service = "Airstyle w/brush", Price = 5.00M, CompID = 1 });
                Services.Add(new ServModel { _uid = 17, TransCode = ItemType.Service, Service = "Beard Trim", Price = 3.00M, CompID = 1 });

                return Services;
            }
            else { return null; }
        }
    }
}

IMP: This code implements explicit index. If you would like to test with Sequential Index then

  1. Uncomment following two lines:
int i = 0;
i++;
  1. Comment this line:
<input type="hidden" name='Servs.Index' value='@itm.ServID' />
  1. Replace Servs[@itm.ServID] with Servs[@i]
Share Improve this question edited Mar 22 at 6:49 NP3 asked Mar 21 at 15:10 NP3NP3 66410 silver badges23 bronze badges 10
  • Suspect that the problem was due to Servs.Index. What if you comment/remove the <input> element for Servs.Index? Also as you are sending the Servs array, the index should be start from 0, 1, 2..., but from the screenshot you are skipping the index as using the ID. – Yong Shun Commented Mar 21 at 15:33
  • How is your ServProdModel defined? As @YongShun commented, try commenting out the <input> tag for Servs.Index and check the collection. – SoftwareDveloper Commented Mar 21 at 16:03
  • @YongShun: This is a documented method. Please check this: learnrazorpages/razor-pages/… I tried the first method using for loop but it did not work (same results). Then I tried the second one i.e. using Explicit Index. In the later case the Index can be any number, in fact it can be a string or other datatype as well. – NP3 Commented Mar 21 at 16:29
  • Is your ServID a unique value? – SoftwareDveloper Commented Mar 21 at 16:46
  • @SoftwareDveloper: Yes ServID is Unique. – NP3 Commented Mar 21 at 16:50
 |  Show 5 more comments

1 Answer 1

Reset to default 1

For model binding, you have to work with the properties (getter/setter) instead of field.

Change for those fields that you are passing the value from the view to properties in your ServModel class.

public class ServModel
{
    public decimal Price { get; set; } = 0.00M;
    public int Qty { get; set; } = 0;
    public int PromoID { get; set; } = 1;

    ...
    
    public int ServID 
    { 
        get { return _uid; } 
        set { _uid = value; } 
    }

    ...
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744344308a4569589.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信