简体   繁体   中英

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

I'm new to web app development, and I wonder if somebody could point me in the right direction with this? I think I know what the problem is, but I don't know how to fix it.

What I have is a complex model where a header can contain a child, and each child can itself have its own children (of different types).

My demo code shown here has a header, and two lists. If I click the button to add a child item to either the first list or a second list, the initial problem I have is that the items don't appear. The other problem is that I don't appear to be able to keep on adding items to either list.

To try to see what is going on, I added a section to show the innerHTML to see what the data is - and this illustrates that 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.

I think the problem is that the Model isn't updated to reflect the new items - but I understand that we shouldn't be able to do that with Javascript anyway. So, I think I need to separate all my partial views, so I have a main partial view containing lots of partial views, and when my Ajax call is successful, refresh the MAIN partial view.

My PageModel 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; }
    }
}

My Index page

@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>

My Main partial view (_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>
}

My first list partial view (_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>

My second list partial view (_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>

Any help would be appreciated.

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.

If you set a break point in the AddListItem() function or the OnPostAddNewSecondListChildItem() method, you will find that the myHeader object is the original object, it doesn't contain the new items. I think the issue is related that since you are using JQuery ajax to load the partial view, the Index page not refresh and the Model is not update, so, when using this script( var model = @Json.Serialize(Model.MyHeader); ) to get the model data, it will always get the original value.

To solve this issue, you could try to use session to store the latest data, before add new items, you could get the latest data first, then add new item.

Change your code as below ( Updated ):

    <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>

and :

    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);
    }

screenshot ( Updated ):

在此处输入图片说明

To use session in asp.net core application, see Session and state management in ASP.NET Core

[Note] if you want to use the session to store complex object, you have to add the SessionExtensions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM