ASP.NET 核心 3.1 Razor 页面 - 添加不同的 child objects 到 header

ASP.NET Core 3.1 Razor pages - adding different child objects to header

我是 Web 应用程序开发的新手,我想知道是否有人可以为此指出正确的方向?我想我知道问题出在哪里,但我不知道如何解决它。

我有一个复杂的模型,其中 header 可以包含 child,每个 child 本身可以有自己的 children(不同类型).

此处显示的我的演示代码有一个 header 和两个列表。如果我单击按钮将 child 项目添加到第一个列表或第二个列表,我遇到的最初问题是这些项目没有出现。另一个问题是我似乎无法继续向任一列表添加项目。

为了尝试查看发生了什么,我添加了一个部分来显示 innerHTML查看数据是什么——这说明如果我点击添加到第一个列表,第一个列表会出现一些数据,但是如果我点击添加到第二个列表,新数据从第一个列表中消失,而新的项目按预期出现在第二个列表中。

我认为问题是模型没有更新以反映新项目 - 但我知道我们不应该能够用 Javascript 做到这一点。所以,我想我需要把我所有的局部视图分开,所以我有一个包含很多局部视图的主局部视图,当我的 Ajax 调用成功时,刷新主局部视图。

我的页面模型class

namespace ParentChildDemo.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;

        [BindProperty]
        public Header MyHeader { get; set; } = new Header();

        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
            MyHeader.Id = 1;
            MyHeader.MyHeaderProperty = "HeaderTest1";
            MyHeader.ChildOfHeader.Add(new ChildOfHeader());

            for (int i = 1; i <= 3; i++)
            {
                var childOfChild = new ChildOfChild()
                {
                    Id = i,
                    HeaderId = MyHeader.Id,
                    MyChildProperty = $"FirstChildTest{i}"
                };

                MyHeader.ChildOfHeader[0].MyFirstChildList.Add(childOfChild);
            }

            for (int i = 1; i <= 2; i++)
            {
                var childOfChild = new ChildOfChild()
                {
                    Id = i,
                    HeaderId = MyHeader.Id,
                    MyChildProperty = $"SecondChildTest{i}"
                };

                MyHeader.ChildOfHeader[0].MySecondChildList.Add(childOfChild);
            }
        }

        public PartialViewResult OnPostAddNewFirstListChildItem([FromBody] Header myHeader)
        {
            if (myHeader.ChildOfHeader[0].MyFirstChildList == null)
                myHeader.ChildOfHeader[0].MyFirstChildList = new List<ChildOfChild>();

            var childId = myHeader.ChildOfHeader[0].MyFirstChildList.Count + 1;

            myHeader.ChildOfHeader[0].MyFirstChildList.Add(new ChildOfChild
            {
                Id = childId,
                HeaderId = myHeader.Id,
                MyChildProperty = $"FirstChildTest{childId}"
            });

            var partialView = "_ListPartialView";
            //var partialView = "_FirstListItemPartial";

            var data = new IndexModel(_logger);
            data.MyHeader = myHeader;

            var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader } };
            //var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader.ChildOfHeader[0].MyFirstChildList } };
            myViewData.Model = data;

            var partialViewResult = new PartialViewResult()
            {
                ViewName = partialView,
                ViewData = myViewData,
            };

            return partialViewResult;
        }

        public PartialViewResult OnPostAddNewSecondListChildItem([FromBody] Header myHeader)
        {
            if (myHeader.ChildOfHeader[0].MySecondChildList == null)
                myHeader.ChildOfHeader[0].MySecondChildList = new List<ChildOfChild>();

            var childId = myHeader.ChildOfHeader[0].MySecondChildList.Count + 1;

            myHeader.ChildOfHeader[0].MySecondChildList.Add(new ChildOfChild
            {
                Id = childId,
                HeaderId = myHeader.Id,
                MyChildProperty = $"SecondChildTest{childId}"
            });

            var partialView = "_ListPartialView";
            //var partialView = "_SecondListItemPartial";

            var data = new IndexModel(_logger);
            data.MyHeader = myHeader;

            var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader } };
            //var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader.ChildOfHeader[0].MySecondChildList } };
            myViewData.Model = data;

            var partialViewResult = new PartialViewResult()
            {
                ViewName = partialView,
                ViewData = myViewData,
            };

            return partialViewResult;
        }
    }

    public class Header
    {
        public int Id { get; set; }
        public string MyHeaderProperty { get; set; }
        public List<ChildOfHeader> ChildOfHeader { get; set; } = new List<ChildOfHeader>();
    }

    public class ChildOfHeader
    {
        public List<ChildOfChild> MyFirstChildList { get; set; } = new List<ChildOfChild>();
        public List<ChildOfChild> MySecondChildList { get; set; } = new List<ChildOfChild>();
    }

    public class ChildOfChild
    {
        public int Id { get; set; }
        public int HeaderId { get; set; }
        public string MyChildProperty { get; set; }
    }
}

我的索引页

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<div>
    <a class="btn btn-sm btn-info text-white" onclick="AddListItem(1)">Add Child to First list</a>
    <a class="btn btn-sm btn-info text-white" onclick="AddListItem(2)">Add Child to Second list</a>
</div>

<br />

<div>
    <div><b>MyHeaderProperty value:</b> @Model.MyHeader.MyHeaderProperty</div>
    <br />
    <div class="container">
        <div class="row">
            <div class="col-6 font-weight-bold">First List</div>
            <div class="col-6 font-weight-bold">Second List</div>
        </div>
        <div class="ListPartialView">
            <partial name="_ListPartialView" model="@Model" />
        </div>
    </div>
    <br />
    <div class="bg-warning" id="HtmlContent"></div>
    
    @Html.AntiForgeryToken()
</div>

<script type="text/javascript">
    function AddListItem(listNumber) {
        var model = @Json.Serialize(Model.MyHeader);

        var handler;
        var partialView;

        if (listNumber == 1) {
            handler = "?handler=AddNewFirstListChildItem";
            partialView = "#ListPartialView";//"#FirstListPartial";
        }
        else {
            handler = "?handler=AddNewSecondListChildItem";
            partialView = "#ListPartialView";//"#SecondListPartial";
        }

        $.ajax({
            type: "POST",
            url: handler,
            data: JSON.stringify(model),
            dataType: "html",
            contentType: "application/json",
            headers: {
                RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val()
            },
            success: function (result) {
                document.getElementById("HtmlContent").innerHTML = result.toString();
                $(partialView).html(result);
            },
            failure: function (result) {
                alert("Failed");
            }
        });
    }
</script>

我的主局部视图 (_ListPartialView)

@model IndexModel

@for (int i = 0; i < Model.MyHeader.ChildOfHeader.Count; i++)
{
    ViewData["ChildIndex"] = i;

    <div class="row">
        <div id="FirstListPartial" class="col-6">
            <partial name="_FirstListItemPartial" model="@Model" view-data="ViewData" />
        </div>
        <div id="SecondListPartial" class="col-6">
            <partial name="_SecondListItemPartial" model="@Model" view-data="ViewData" />
        </div>
    </div>
}

我的第一个列表部分视图 (_FirstListItemPartial)

@model IndexModel

@{ 
    var indexId = (int)ViewData["ChildIndex"];
}


<table>
    @foreach (var myChildItem in Model.MyHeader.ChildOfHeader[indexId].MyFirstChildList)
    {
        <tr>
            <td>@myChildItem.Id</td>
            <td>@myChildItem.MyChildProperty</td>
        </tr>
    }
</table>

我的第二个列表部分视图 (_SecondListItemPartial)

@model IndexModel

@{
    var indexId = (int)ViewData["ChildIndex"];
}

<table>
    @foreach (var myChildItem in Model.MyHeader.ChildOfHeader[indexId].MySecondChildList)
    {
        <tr>
            <td>@myChildItem.Id</td>
            <td>@myChildItem.MyChildProperty</td>
        </tr>
    }
</table>

如有任何帮助,我们将不胜感激。

if I click to add to the first list, some data appears for the first list, but if I click to add to the second list, the new data disappears from the first list whilst the new item appears as expected in the second list.

如果在AddListItem() 函数或OnPostAddNewSecondListChildItem() 方法中设置断点,您会发现myHeader 对象是原始对象,它不包含新项目。我认为这个问题是相关的,因为你正在使用 JQuery ajax 来加载局部视图,索引页面没有刷新并且模型没有更新,所以,当使用这个脚本时(var model = @Json.Serialize(Model.MyHeader);) 获取模型数据,总是获取原始值。

解决这个问题,可以尝试使用session来存储最新数据,在添加新item之前,可以先获取最新数据,再添加新item。

更改您的代码如下(已更新):

    <script type="text/javascript">
        function AddListItem(listNumber) {
            //var model = @Json.Serialize(Model.MyHeader);  
            var model;
            //get the latest daa
            $.ajax({
                type: "get",
                url: "?handler=GetLatestData",
                async: false,
                contentType: "application/json", 
                success: function (data) {
                    model = data;
                }
            });  
            var handler;
            var partialView;

            if (listNumber == 1) {
                handler = "?handler=AddNewFirstListChildItem";
                partialView = "#FirstListPartial";//"#ListPartialView";
            }
            else {
                handler = "?handler=AddNewSecondListChildItem";
                partialView = "#SecondListPartial";//"#SecondListPartial";
            }

            $.ajax({
                type: "POST",
                url: handler,
                data: JSON.stringify(model),
                dataType: "html",
                contentType: "application/json",
                headers: {
                    RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val()
                },
                success: function (result) {
                    //document.getElementById("HtmlContent").innerHTML = result.toString();
                    $(partialView).html("");
                    $(partialView).html(result);
                },
                failure: function (result) {
                    alert("Failed");
                }
            });
        }
    </script>

和:

    public void OnGet()
    {
        MyHeader.Id = 1;
        MyHeader.MyHeaderProperty = "HeaderTest1";
        MyHeader.ChildOfHeader.Add(new ChildOfHeader());

        for (int i = 1; i <= 3; i++)
        {
            var childOfChild = new ChildOfChild()
            {
                Id = i,
                HeaderId = MyHeader.Id,
                MyChildProperty = $"FirstChildTest{i}"
            };

            MyHeader.ChildOfHeader[0].MyFirstChildList.Add(childOfChild);
        }

        for (int i = 1; i <= 2; i++)
        {
            var childOfChild = new ChildOfChild()
            {
                Id = i,
                HeaderId = MyHeader.Id,
                MyChildProperty = $"SecondChildTest{i}"
            };

            MyHeader.ChildOfHeader[0].MySecondChildList.Add(childOfChild);
        }

        if (HttpContext.Session.Get<Header>("HeaderData") == null)
        {
            HttpContext.Session.Set<Header>("HeaderData", MyHeader);
        }
    }

    public PartialViewResult OnPostAddNewFirstListChildItem([FromBody] Header myHeader)
    {
        if (myHeader.ChildOfHeader[0].MyFirstChildList == null)
            myHeader.ChildOfHeader[0].MyFirstChildList = new List<ChildOfChild>();

        var childId = myHeader.ChildOfHeader[0].MyFirstChildList.Count + 1;

        myHeader.ChildOfHeader[0].MyFirstChildList.Add(new ChildOfChild
        {
            Id = childId,
            HeaderId = myHeader.Id,
            MyChildProperty = $"FirstChildTest{childId}"
        });

        //var partialView = "_ListPartialView";
        var partialView = "_FirstListItemPartial";

        var data = new IndexModel(_logger);
        data.MyHeader = myHeader;

        var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader } };
        //var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader.ChildOfHeader[0].MyFirstChildList } };
        myViewData.Model = data;

        var partialViewResult = new PartialViewResult()
        {
            ViewName = partialView,
            ViewData = myViewData,
        };
        
        HttpContext.Session.Set<Header>("HeaderData", myHeader);

        return partialViewResult;
    }

    public PartialViewResult OnPostAddNewSecondListChildItem([FromBody] Header myHeader)
    {
        if (myHeader.ChildOfHeader[0].MySecondChildList == null)
            myHeader.ChildOfHeader[0].MySecondChildList = new List<ChildOfChild>();

        var childId = myHeader.ChildOfHeader[0].MySecondChildList.Count + 1;

        myHeader.ChildOfHeader[0].MySecondChildList.Add(new ChildOfChild
        {
            Id = childId,
            HeaderId = myHeader.Id,
            MyChildProperty = $"SecondChildTest{childId}"
        });

        //var partialView = "_ListPartialView";
        var partialView = "_SecondListItemPartial";

        var data = new IndexModel(_logger);
        data.MyHeader = myHeader;

        var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader } };
        //var myViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { partialView, myHeader.ChildOfHeader[0].MySecondChildList } };
        myViewData.Model = data;

        var partialViewResult = new PartialViewResult()
        {
            ViewName = partialView,
            ViewData = myViewData,
        };
        HttpContext.Session.Set<Header>("HeaderData", myHeader);
        return partialViewResult;
    }
    public JsonResult OnGetGetLatestData()
    {
        var result = new Header();
        if (HttpContext.Session.Get<Header>("HeaderData") != null)
        {
            result = HttpContext.Session.Get<Header>("HeaderData");
        };
        return new JsonResult(result);
    }

截图(已更新):

要在 asp.net 核心应用程序中使用会话,请参阅 Session and state management in ASP.NET Core

[注意]如果要使用session存储复杂的对象,必须添加SessionExtensions。