在 post .net Core Razor 页面上与数据绑定模型作斗争

Posted

技术标签:

【中文标题】在 post .net Core Razor 页面上与数据绑定模型作斗争【英文标题】:Struggling with Data Binding Model on post .netCore Razor Pages 【发布时间】:2020-04-07 02:59:19 【问题描述】:

我目前正在努力获取数据以绑定到发布事件。

我正在使用 EF 核心将数据加载到用于加载和显示数据列表的对象。

  <div class="container">
    <form method="post">
        @if (Model.HeadersHasData)
        
           // unnessary codes 


            @foreach (var item in Model.DisplayDataHeaders)
            
                <div class="container mb-2">
                    <div class="row border-dark border-top">
                        <div class="col-sm-1">@html.DisplayFor(c => item.ServiceAbbr)</div>
                        <div class="col-sm-4">@Html.DisplayFor(c => item.ServiceName)</div>
                        <div class="col-sm-3">@Html.DisplayFor(c => item.Qty) Hours/IPs/Locations</div>
                        <div class="col-sm-2">@Html.DisplayFor(c => item.Description) Duration</div>
                    </div>
                    @if (Model.DetailsHasData)
                    
                        @foreach (var details in Model.DisplayDataDetails)
                        
                            @if (details.ServiceId == item.ServicesId)
                            
                                <div class="row">
                                    <input type="hidden"
                                           name="DisplayDataDetails.Index"
                                           value="@details.TaskAssignedId" />
                                    <input type="hidden"
                                           name="DisplayDataDetails[@details.TaskAssignedId].TaskAssignedId"
                                           value="@details.TaskAssignedId" />
                                    <div class="col-sm-1">
                                        <input type="checkbox"
                                               name="DisplayDataDetails[@details.TaskAssignedId].TaskComplete"
                                               value="@details.TaskComplete"
                                               checked=@details.TaskComplete
                                               disabled="@details.AccessGranted" />
                                    </div>
                                    <div class="col-sm-2">@details.TaskName</div>
                                    <div class="col-sm-1">@details.TaskStartDate.ToShortDateString()</div>
                                    <div class="col-sm-1">@details.TaskEndDate.ToShortDateString()</div>
                                    <div class="col-sm-2">@details.TechLead</div>
                                    <div class="col-sm-2">@details.Tech2</div>
                                    <div class="col-sm">@details.Tech3</div>
                                </div>
                            
                        
                    
                </div>
            

        
        <button type="submit">Test</button>
    </form>
</div>

这会正确显示数据和所有内容。

我现在正在处理用户更改当前使用的复选框时的交互(最终将尝试计算复选框的更改事件)。

我已经应用了 [bindProperty],但是每次通过 DisplayDataDetails 对象进行调试时,我都会得到一个计数为 13 的对象,这是预期的正确行数,但所有行都是空的。

我对自己错过的内容感到非常困惑,因为我在 https://www.learnrazorpages.com/razor-pages/forms/checkboxes 上阅读的所有内容 和 https://www.learnrazorpages.com/razor-pages/model-binding

表明这应该是一个非常直接的操作。

     [BindProperty]
        public List<Detail> DisplayDataDetails  get; set; 

  public async Task OnPostAsync(int? saleId)
        
            var test = DisplayDataDetails;
            //await LoadDataAsync(saleId);

            var pause= "pause";

        

任何助手或建议将不胜感激。

更新 1

只是想添加和更新,创建了一个单独的测试项目,并且能够通过 https://www.learnrazorpages.com/razor-pages/model-binding 中的绑定复杂集合示例我修改了视图以匹配我的用例,即使使用复选框也能够取回数据使用数据绑定回发的值。

这行得通 .cs

namespace RazorPages

    public class ModeContactTestModel : PageModel
    
        [BindProperty]
        public List<Contact> Contacts  get; set;  = new List<Contact>();
        public string stubFirstName  get; private set; 
        public string stubLastName  get; private set; 

        public void OnGet()
        
           LoadTestData();
        

        private void LoadTestData()
        
            //Contacts = 
            stubFirstName = "john";
            stubLastName = "Doe";
            for (var i = 0; i < 5; i++)
            
                Contacts.Add(new Contact
                
                    ContactId = i,
                    FirstName = stubFirstName,
                    LastName = stubLastName,
                    Email = stubFirstName + "." + stubLastName + i + "Test@email.com",
                    Enabled =  (i % 2 == 0) ?  true : false
                );
            

        

        public void OnPost()
        
            // process the contacts
        
    

    public class Contact
    
        public int ContactId  get; set; 
        public string FirstName  get; set; 
        public string LastName  get; set; 
        public string Email  get; set; 
        public bool Enabled  get; set; 
    

.cshtml

@page
@model RazorPages.ModeContactTestModel
@
    ViewData["Title"] = "ModelContactTest";


<h1>ModelContactTest</h1>
<form method="post">
    <table class="table">

        <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
            <th>Enabled</th>
        </tr>
        @foreach (var contact in Model.Contacts)
        
            <tr>
                <td>
                    <input type="hidden" name="Contacts.Index" value="@contact.ContactId" />
                    <input type="hidden" name="Contacts[@contact.ContactId].ContactId" value="@contact.ContactId" />
                    @*@contact.ContactId*@
                </td>
                <td><input name="Contacts[@contact.ContactId].FirstName" value="@contact.FirstName" /></td>
                <td><input name="Contacts[@contact.ContactId].LastName" value="@contact.LastName" /></td>
                <td><input name="Contacts[@contact.ContactId].Email" value="@contact.Email" /></td>
                <td>
                    <input type="checkbox" 
                           name="Contacts[@contact.ContactId].Enabled" 
                           value="true" 
                           checked="@contact.Enabled"/></td>
            </tr>
        
    </table>
    <button>Update</button>
</form>

我创建了一个新的演示 razorPage 来开始重写我的页面我从苹果开始应用到我的工作项目中......数据绑定似乎仍然不起作用

这没有任何作用,因为我没有看到任何不同之处。

.cs

namespace Project.Web.Razor

    public class ################: PageModel
    
        [BindProperty]
        public List<Detail> Details  get; set; // = new List<Detail>(); //

        public void OnGet()
        
            LoadData();
        

        private void LoadData()
        
            Details = new List<Detail>();
            for (var i = 0; i < 5; i++)
            
                Details.Add(new Detail
                
                    AssignedId = i,
                    Name = "Dev Testing Task " + i,
                    Complete = (i % 2 == 0) ? true : false,
                    StartDate = Convert.ToDateTime("01/01/2019"),
                    EndDate = Convert.ToDateTime("01/01/2019"),
                    Lead = "John Doe",
                    //SaleId = i+1,
                    //ServiceId = i + 2,
                    AccessGranted = (i % 2 == 0) ? true : false,

                );
            
        

        public void OnPost()
        
            // process the TaskDetails
        
    

    public class Detail
    
        public int AssignedId  get; internal set; 
        public string  Name  get; internal set; 
        public bool Complete  get; internal set; 
        public DateTime  StartDate  get; internal set; 
        public DateTime  EndDate  get; internal set; 
        public string  Lead  get; internal set; 
        public bool AccessGranted  get; internal set; 
    

.cshtml

@page
@model ##########################
@
    ViewData["Title"] = "#####################";


<h1>EngagementingDetails</h1>
<form method="post">
    <table class="table">
        @foreach (var Detail in Model.Details)
        
            <tr class="row">
                <td>
                    <input type="hidden" name="Details.Index" value="@Detail.AssignedId" />
                    <input type="hidden" name="Details[@Detail.AssignedId].AssignedId" value="@Detail.AssignedId" />
                </td>

                <td class="col">
                    <input type="checkbox" name="Details[@Detail.AssignedId].Complete" value="true" checked="@Detail.Complete" disabled="@Detail.AccessGranted" />
                </td>
                <td class="col">
                    <input type="hidden" name="Details[@Detail.AssignedId].Name" value="@Detail.Name" />
                    @Detail.Name
                </td>
                <td class="col">
                    <input type="hidden"
                           name="Details[@Detail.AssignedId].StartDate"
                           value="@Detail.StartDate" />
                    @Detail.StartDate.ToShortDateString()
                </td>
                <td class="col">
                    <input type="hidden"
                           name="Details[@Detail.AssignedId].EndDate"
                           value="@Detail.EndDate" />
                    @Detail.EndDate.ToShortDateString()
                </td>
                <td class="col">
                    <input type="hidden"
                           name="Details[@Detail.AssignedId].TechLead"
                           value="@Detail.TechLead" />
                    @Detail.TechLead
                </td>

            </tr>
        
    </table>
    <button>Update</button>
</form>


这真的让我很困惑,为什么我可以让它在一个项目中工作但不能在单击更新和调试 OnPost 事件时得到它返回一个计数为 5 的列表,但每个项目在我的工作项目中为空测试合同项目它返回列表计数 5,每个对象中的数据都符合预期。不知道我在两者之间做了什么不同。

更新 2

关于最后一条评论,我为测试做了以下更改

.CSHTML

<form method="post">
    <table class="table">
        <tr class="row">
            <th></th>
            <th class="col">Complete</th>
            <th class="col">Name</th>
            @*<th class="col">StartDate</th>
            <th class="col">EndDate</th>
            <th class="col">Lead</th>*@
        </tr>

        @foreach (var detail in Model.Details)
        
            <tr class="form-group row">
                <td >
                    <input type="hidden" 
                           name="Details.Index" 
                           value="@detail.AssignedId" />
                    <input type="hidden" 
                           name="Details[@detail.AssignedId].AssignedId" 
                           value="@detail.AssignedId" />
                </td>

                <td class="col">
                    <input class="form-check-input"
                           type="checkbox"
                           name="Details[@detail.AssignedId].Complete"
                           value="true"
                           checked="@detail.Complete"
                           />
                </td>
                <td class="col">
                    <input class="form-control form-control-sm"
                           name="Details[@detail.AssignedId].Name" 
                           value="@detail.Name" readonly />                    
                </td>

            </tr>
        
    </table>
    <button>Update</button>
</form>

.cs

  [BindProperty]
        public List<Detail> Details  get; set;  = new List<Detail>(); //

        public void OnGet()
        
            LoadData();
        

        private void LoadData()
        
            //Details = new List<Detail>();
            for (var i = 0; i < 5; i++)
            
                Details.Add(new Detail
                
                    AssignedId = i,
                    Name = "Dev Testing Task " + i,
                    Complete = (i % 2 == 0) ? true : false,
                    //StartDate = Convert.ToDateTime("01/01/2019").AddDays(i + 1).AddMonths((i)+1),
                    //EndDate = Convert.ToDateTime("01/01/2019").AddDays((i + 1) + 15).AddMonths((i) + 1),
                    //Lead = "John Doe",
                    //SaleId = i+1,
                    //ServiceId = i + 2,
                    //AccessGranted = (i % 2 == 0) ? false : true,

                );
            
        

        public void OnPost()
        
            // process the TaskDetails

            if(Details is null)
            
                LoadData();
            else if( Details[0] == null)
            
                Details = new List<Detail>();
                LoadData();
            
            else
            
                Details = new List<Detail>();
                LoadData();
            



        
    

    public class Detail
    
        public int AssignedId  get; internal set; 
        public string Name  get; internal set; 
        public bool Complete  get; internal set; 
        //public DateTime StartDate  get; internal set; 
        //public DateTime EndDate  get; internal set; 
        //public string Lead  get; internal set; 
        //public bool AccessGranted  get; internal set; 
    

在调试时,我仍然返回 onPost 列表详细信息,计数为 5,但其中的每个项目都是空的。 IE。 Details[0] = null、Details1 = null 等。

刚刚跑了提琴手,它似乎正在发送对象

Fidder Output snippet

现在我想知道绑定属性是否无法获取发布事件的数据。

【问题讨论】:

首先简化您返回的数据。 . .如果您只有一个 Id、一个名称和完整的字段,它会返回值吗? @PaulGibson 我将反对简化为三个字段 Id、Complete 和 Name 甚至删除了 accessGranted,即如果不是登录用户则禁用复选框。我仍然得到一个 List
Details 返回,计数为 5,这表明它是绑定对象,但每个项目都返回 null Details = count = 5 details[0] = null ,details[1] = null 等等。不确定这在我的测试项目中有效,而不是在这个项目中
【参考方案1】:

我让它工作了

我最终使用我的合同测试作为模板从头开始完全重写它。在每次重大更改后测试绑定。我在我的对象中添加了一个 IndexId 列,以便我可以显式设置 List 索引,我认为这是我之前的主要问题。我相信我的问题与我的两个 foreach 循环有关,我需要在没有定义严格索引的情况下生成视图,这会导致绑定失败......至少我认为这是我的问题。

最终的 CSHTML

        <form method="post">
            @foreach (var row in Model.DetailsHeaders)
            
                <table class="table table-sm">
                    <thead>
                        <tr>
                            <th colspan="2" scope="colgroup">@Html.DisplayFor(c => row.ServiceAbbr)</th>
                            <th colspan="2" scope="colgroup">@Html.DisplayFor(c => row.ServiceName)</th>
                            <th colspan="2" scope="colgroup">@Html.DisplayFor(c => row.Qty) Hours/IPs/Locations</th>
                            <th colspan="2" scope="colgroup">@Html.DisplayFor(c => row.Description) Duration</th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (var detail in Model.Details)
                        
                            if (detail.ServiceId == row.ServicesId)
                            
                                <tr>
                                    <td scope="row">
                                        <input type="hidden"
                                               name="Details.IndexId"
                                               value="@detail.IndexId" />
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].IndexId"
                                               value="@detail.IndexId" />
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].AssignedId"
                                               value="@detail.AssignedId" />
                                        @*@Html.DisplayFor(r => detail.AssignedId)*@
                                    </td>
                                    <td>
                                        <input type="checkbox"
                                               name="Details[@detail.IndexId].Complete"
                                               value="true"
                                               checked="@detail.Complete"
                                               disabled="@detail.EditAccess" />
                                    </td>
                                    <td>
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].Name"
                                               value="@detail.Name"
                                               readonly />
                                        @Html.DisplayFor(r => detail.Name)
                                    </td>
                                    <td>
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].StartDate"
                                               value="@detail.StartDate"
                                               readonly />
                                        @Html.DisplayFor(r => detail.StartDate)
                                    </td>
                                    <td>
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].EndDate"
                                               value="@detail.EndDate"
                                               readonly />
                                        @Html.DisplayFor(r => detail.EndDate)
                                    </td>
                                    <td>
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].LeadTech"
                                               value="@detail.LeadTech"
                                               readonly />
                                        @Html.DisplayFor(r => detail.LeadTech)
                                    </td>
                                    <td>
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].Tech2"
                                               value="@detail.Tech2"
                                               readonly />
                                        @Html.DisplayFor(r => detail.Tech2)
                                    </td>
                                    <td>
                                        <input type="hidden"
                                               name="Details[@detail.IndexId].Tech3"
                                               value="@detail.Tech3"
                                               readonly />
                                        @Html.DisplayFor(r => detail.Tech3)
                                    </td>
                                </tr>
                            
                        
                    </tbody>
                </table>
            
            <button>Update</button>
        </form>

【讨论】:

以上是关于在 post .net Core Razor 页面上与数据绑定模型作斗争的主要内容,如果未能解决你的问题,请参考以下文章

覆盖 asp.net core razor 页面中的 razor 视图

如何在 ASP.Net Core Razor 页面上重定向

.NET Core 无法添加 Razor 页面

ASP .NET Core Razor 页面中的授权

在 ASP.Net Core Razor 页面中添加 Toast

ASP.NET Core Razor 页面与完整 MVC Core [关闭]