在 Asp.Net Core MVC 项目中未生成嵌套子子菜单

问题描述 投票:0回答:1

我正在尝试生成 3 级嵌套菜单,以便为我的应用程序创建导航栏。我写在下面的代码。但代码只返回顶部菜单和中间菜单。名为底部的第三个菜单未显示。我的项目是在 Asp.Net Core MVC 中。

SQL 表 - MenuMasterDemo

MenuMasterDemo.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.EntityFrameworkCore;
    
    namespace SampleApp.Models;
    
    [Table("MenuMasterDemo")]
    public partial class MenuMasterDemo
    {
        [Key]
        [Column("MenuID")]
        public long MenuId { get; set; }
    
        [Unicode(false)]
        public string? Menu { get; set; }
    
        [Column("ParentMenuID")]
        public long? ParentMenuId { get; set; }
    
        // Property to hold child items
        [NotMapped]
        public List<MenuMasterDemo>? Children { get; set; }
    
    }

应用程序菜单视图组件

using Microsoft.AspNetCore.Mvc;
using SampleApp.Data;

namespace SampleApp.ViewComponents
{
    public class AppMenuViewComponent : ViewComponent
    {
        private readonly SampleContext _context;

        public AppMenuViewComponent(SampleContext context)
        {
            _context = context;
        }
        public IViewComponentResult Invoke()
        {
            return View(_context.MenuMasterDemos.ToList());
        }

    }
}

AppMenuViewComponent - Default.cshtml

@model IEnumerable<MenuMasterDemo>

<!-- Side navigation menu -->
<div class="d-flex flex-column align-items-start align-items-md-center px-3 pt-2 text-white">
    <ul class="nav flex-column nav-pills">
        @foreach (var item in Model.Where(x => x.ParentMenuId == null)) // Filter top-level menus
        {
            <li class="nav-item">
                <a class="nav-link" data-bs-toggle="collapse" href="#[email protected]" role="button" aria-expanded="false" aria-controls="[email protected]">@item.Menu</a>
                <div class="collapse" id="[email protected]">
                    <ul class="nav flex-column ms-3">
                        <!-- Invoke RenderSubMenuItemsViewComponent for each top-level menu item -->
                        @await Component.InvokeAsync("RenderSubMenuItems", new { model = Model, parentMenuId = item.MenuId })
                    </ul>
                </div>
            </li>
        }
    </ul>
</div>

RenderSubMenuItemsViewComponent

using Microsoft.AspNetCore.Mvc;
using SampleApp.Data;
using SampleApp.Models;

namespace SampleApp.ViewComponents
{
    public class RenderSubMenuItemsViewComponent : ViewComponent
    {
        private readonly SampleContext _context;

        public RenderSubMenuItemsViewComponent(SampleContext context)
        {
            _context = context;
        }
        public IViewComponentResult Invoke(IEnumerable<MenuMasterDemo> model, long parentMenuId)
        {

            var submenuItems = model.Where(x => x.ParentMenuId == parentMenuId).ToList();

            foreach (var submenuItem in submenuItems)
            {
                submenuItem.Children = Invoke(model, submenuItem.MenuId) as List<MenuMasterDemo>;
            }


            return View(submenuItems);
        }
    }
}

RenderSubMenuItemsViewComponent - Default.cshtml

@model IEnumerable<MenuMasterDemo>

    <ul class="nav flex-column ms-3">
        @foreach (var subItem in Model)
        {
            <li class="nav-item">
                <a class="nav-link" href="#">@subItem.Menu</a>
                @if (subItem.Children != null && subItem.Children.Any())
                {
                    <!-- Recursively render children -->
                    @await Component.InvokeAsync("RenderSubMenuItems", new { model = subItem.Children, parentMenuId = subItem.MenuId })
                }
            </li>
        }
    </ul>

_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - SampleApp</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/SampleApp.styles.css" asp-append-version="true" />

</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div>
                @await Component.InvokeAsync("AppMenu")
            </div>
            <div class="col py-3">
                @RenderBody()
            </div>
        </div>
    </div>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
c# asp.net-mvc-4 model-view-controller asp.net-core-mvc
1个回答
0
投票

我在我这边使用了模拟数据,下面的代码运行良好。

我首先使用您的代码

submenuItem.Children = Invoke(model, submenuItem.MenuId) as List<MenuMasterDemo>;
重现了您的问题,使用
submenuItem.Children = model.Where(x => x.ParentMenuId == submenuItem.MenuId).ToList();
后,可以看到底部菜单。这是因为 Invoke 方法是由组件调用的,并且应该返回 View。但我们所需要的只是设置 Children 属性。

using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;

namespace WebAppMvc2.ViewComponents
{
    public class AppMenuViewComponent : ViewComponent
    {
        public IViewComponentResult Invoke()
        {
            var list = new List<MenuMasterDemo> { 
                new MenuMasterDemo{
                    MenuId = 1,
                    Menu = "top",
                    ParentMenuId = null
                },
                new MenuMasterDemo
                {
                    MenuId = 2,
                    Menu = "middle1",
                    ParentMenuId = 1
                },
                new MenuMasterDemo
                {
                    MenuId = 3,
                    Menu = "middle2",
                    ParentMenuId = 1
                },
                new MenuMasterDemo
                {
                    MenuId = 4,
                    Menu = "bottom1",
                    ParentMenuId = 2
                },
                new MenuMasterDemo
                {
                    MenuId = 5,
                    Menu = "bottom2",
                    ParentMenuId = 3
                }
            };
            return View(list);
        }

    }

    public class RenderSubMenuItems : ViewComponent 
    {
        public IViewComponentResult Invoke(IEnumerable<MenuMasterDemo> model, long parentMenuId)
        {
            var submenuItems = model.Where(x => x.ParentMenuId == parentMenuId).ToList();
            foreach (var submenuItem in submenuItems)
            {
                //submenuItem.Children = Invoke(model, submenuItem.MenuId) as List<MenuMasterDemo>;
                submenuItem.Children = model.Where(x => x.ParentMenuId == submenuItem.MenuId).ToList();
            }
            return View(submenuItems);
        }
    }

    public class MenuMasterDemo
    {
        public long MenuId { get; set; }

        public string? Menu { get; set; }

        public long? ParentMenuId { get; set; }

        public List<MenuMasterDemo>? Children { get; set; }

    }
}

© www.soinside.com 2019 - 2024. All rights reserved.