在 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 页面上重定向

如何在 ASP.NET Core Razor 页面中绑定复杂列表

示例 AJAX 回调到 ASP.NET Core Razor 页面

.NET Core Razor Pages中ajax get和post的使用

如何从 ASP.NET Core 的 Razor 视图中获取 Url 中 id 的值

有没有办法在 ASP.NET Core 2.1 中捕获对 Razor 页面不存在的自定义处理程序的 HTTP 请求?