2

我已经看到了几个active为菜单项创建 HTML 帮助器方法的示例。

**Summary:**简而言之,在 MVC 项目中,使用 Twitter Bootstrap,我试图在选择子项时保留可折叠菜单的打开状态。

我正在使用可折叠菜单,active open如果选择了孩子,则需要包含父母的 css(所选项目)。这将确保菜单在正确的位置打开。通过使用另一个 HTML 帮助程序,活动项已设置为active.

菜单的 HTML:

    <div id="sidebar">
        <ul>
            <li class="active"><a href="dashboard.html"><i class="icon-home"></i> <span>Dashboard</span></a></li>
            <li class="submenu">
                <a href="#"><i class="icon-beaker"></i> <span>UI Lab</span> <i class="arrow icon-chevron-right"></i></a>
                <ul>
                    <li><a href="interface.html">Interface Elements</a></li>
                    <li><a href="jquery-ui.html">jQuery UI</a></li>
                    <li><a href="buttons.html">Buttons &amp; icons</a></li>
                </ul>
            </li>
            <li class="submenu">
                <a href="#"><i class="icon-th-list"></i> <span>Form elements</span> <i class="arrow icon-chevron-right"></i></a>
                <ul>
                    <li><a href="form-common.html">Common elements</a></li>
                    <li><a href="form-validation.html">Validation</a></li>
                    <li><a href="form-wizard.html">Wizard</a></li>
                </ul>
            </li>
            <li><a href="tables.html"><i class="icon-th"></i> <span>Tables</span></a></li>
            <li><a href="grid.html"><i class="icon-th-list"></i> <span>Grid Layout</span></a></li>
            <li class="submenu">
                <a href="#"><i class="icon-file"></i> <span>Sample pages</span> <i class="arrow icon-chevron-right"></i></a>
                <ul>
                    <li><a href="invoice.html">Invoice</a></li>
                    <li><a href="chat.html">Support chat</a></li>
                    <li><a href="calendar.html">Calendar</a></li>
                    <li><a href="gallery.html">Gallery</a></li>
                    <li><a href="messages.html">Messages</a></li>
                </ul>
            </li>
            <li>
                <a href="charts.html"><i class="icon-signal"></i> <span>Charts &amp; graphs</span></a>
            </li>
            <li>
                <a href="widgets.html"><i class="icon-inbox"></i> <span>Widgets</span></a>
            </li>
        </ul>

    </div>

这是项目的辅助方法:

    public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper,
        string text,
        string action,
        string controller,
        string iconClass)
    {
        var li = new TagBuilder("li");
        var routeData = htmlHelper.ViewContext.RouteData;
        var currentAction = routeData.GetRequiredString("action");
        var currentController = routeData.GetRequiredString("controller");
        if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
        {
            li.AddCssClass("active");
        }

        var i = new TagBuilder("i");
        i.AddCssClass(iconClass);

        var basePath = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);

        //li.InnerHtml = htmlHelper.ActionLink("<i>something</i>" + text, action, controller).ToHtmlString();
        li.InnerHtml = htmlHelper.Raw(string.Format("<a href=\"{0}/{1}/{2}\"><i class=\"{4}\"></i>{3}</a>", basePath, controller, action, text, iconClass)).ToString();

        return MvcHtmlString.Create(li.ToString());
    }

并像这样实现:

        <div id="sidebar">
            <ul>
                @Html.MenuItem("Dashboard", "Index", "Dashboard", "icon-home")
@*              <li class="active"><a href="dashboard.html"><i class="icon-home"></i> <span>Dashboard</span></a></li>*@
                <li class="submenu">
                    <a href="#"><i class="icon-beaker"></i> <span>UI Lab</span> <i class="arrow icon-chevron-right"></i></a>

                    <ul>
                        <li>@Html.MenuItem("Websites", "Index", "Websites", null)</li>
                        <li><a href="jquery-ui.html">jQuery UI</a></li>
                        <li><a href="buttons.html">Buttons &amp; icons</a></li>
                    </ul>
                </li>
                <li class="submenu">
                    <a href="#"><i class="icon-th-list"></i> <span>Form elements</span> <i class="arrow icon-chevron-right"></i></a>
                    <ul>
                        <li><a href="form-common.html">Common elements</a></li>
                        <li><a href="form-validation.html">Validation</a></li>
                        <li><a href="form-wizard.html">Wizard</a></li>
                    </ul>
                </li>

所以我没有子菜单项。

有没有更简单的方法来实现这一点?

- 更新 -

我猜这可能是最好的。我需要找到某种方法来保留单击时选定的项目以在每个菜单项上引用它,而不是检查控制器/操作是否匹配以将当前项目设置为“活动”。单击时激活的控制器方法,检查当前选定的项目是父项还是子项,如果父项匹配子项,则格式不同?我确信必须有一个更简单的方法。

4

2 回答 2

5

好吧,这是我想出的一个解决方案。

回顾一下,如果项目被选中,这并不像向项目添加“活动”CSS 类那么简单(根据默认的 Bootstrap MVC。在这个解决方案中,我们需要识别父项和子项并识别两者。

在此处输入图像描述

默认页面是仪表板。用户然后单击“配置”以展开菜单,然后选择打开页面的“网站”链接。

这是解决方案:

模型:

public class NavigationMenu
{
    public string Text { get; set; }
    public string Action { get; set; }
    public string Controller { get; set; }
    public string Icon { get; set; }
    public bool Selected { get; set; }

    public List<NavigationMenu> MenuChildren;
}

控制器:

public class NavigationController : Controller
{
    [ChildActionOnly]
    public ActionResult GenerateMenu()
    {
        List<NavigationMenu> menuItems = new List<NavigationMenu>();
        // build the menu
        menuItems.Add(new NavigationMenu
        {
            Text = "Dashboard",
            Action = "",
            Controller = "Dashboard",
            Icon = "icon-home",
            Selected = true,    // default selected menu item
            MenuChildren = null
        });
        menuItems.Add(new NavigationMenu
        {
            Text = "Configuration",
            Action = null,
            Controller = null,
            Icon = "icon-cog",
            MenuChildren = new List<NavigationMenu>{
                new NavigationMenu{
                    Text = "Websites",
                    Action = "",
                    Controller = "Websites",
                    Icon = null,
                    MenuChildren = null
                },
                new NavigationMenu{
                    Text = "Child 2",
                    Action = null,
                    Controller = null,
                    Icon = null,
                    MenuChildren = null
                }
            }
        });
        menuItems.Add(new NavigationMenu
        {
            Text = "Item 2",
            Action = "",
            Controller = "Item2",
            Icon = "icon-random",
            MenuChildren = null
        });
        menuItems.Add(new NavigationMenu
        {
            Text = "Item 3",
            Action = "",
            Controller = "Item3",
            Icon = "icon-certificate",
            MenuChildren = null
        });

        string action = ControllerContext.ParentActionViewContext.RouteData.Values["action"].ToString() == "Index" ? "" : ControllerContext.ParentActionViewContext.RouteData.Values["action"].ToString();
        string controller = ControllerContext.ParentActionViewContext.RouteData.Values["controller"].ToString();

        foreach (var item in menuItems)
        {
            if (item.MenuChildren != null)
            {
                foreach (var cItem in item.MenuChildren)
                {
                    if (cItem.Controller == controller && cItem.Action == action)
                    {
                        cItem.Selected = true;
                        break;
                    }
                    else
                    {
                        cItem.Selected = false;
                    }
                }
            }
            if (item.Controller == controller && item.Action == action)
            {
                item.Selected = true;
                break;
            }
            else
            {
                item.Selected = false;
            }
        }

        return PartialView("~/Views/Shared/_Navigation.cshtml", menuItems); 
    }

}

共享视图:

@model IEnumerable<AdminWebsite.Models.NavigationMenu>
@{
    var basePath = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
}
<div id="sidebar">
    @Html.Raw("<ul>")
        @foreach (var item in Model)
        {
            // if the menu item does not have children then it should be clickable
            if (item.MenuChildren == null & item.Selected)
            {
                <li class="active"><a href="@String.Format("{0}/{1}/{2}", basePath, item.Controller, item.Action)"><i class="@item.Icon"></i> <span>@item.Text</span></a></li>
            }
            else if (item.MenuChildren == null & !item.Selected)
            {
                <li><a href="@String.Format("{0}/{1}/{2}", basePath, item.Controller, item.Action)"><i class="@item.Icon"></i> <span>@item.Text</span></a></li>
            }

            // has children and one of its children is selected
            if (item.MenuChildren != null)
            {
                if (item.MenuChildren.Any(c => c.Selected) == true)
                {                
                    <text><li class="submenu active open"></text>
                }
                else
                {
                    <text><li class="submenu"></text>
                }

                // sub-menu parent
                if (item.MenuChildren != null & item.Selected)
                {
                    <a href="@String.Format("{0}/{1}/{2}", basePath, item.Action, item.Controller)"><i class="@item.Icon"></i> <span>@item.Text</span></a>
                }
                else if (item.MenuChildren != null & !item.Selected)
                {
                    <a href="@String.Format("{0}/{1}/{2}", basePath, item.Action, item.Controller)"><i class="@item.Icon"></i> <span>@item.Text</span></a>
                }

                // children
                <text><ul></text>
                    // iterate through children
                    foreach(var cItem in item.MenuChildren)
                    {
                        if (cItem.MenuChildren == null & cItem.Selected)
                        {
                            <li class="active"><a href="@String.Format("{0}/{1}/{2}", basePath, cItem.Controller, cItem.Action)"><i class="@cItem.Icon"></i> <span>@cItem.Text</span></a></li>
                        }
                        else if (cItem.MenuChildren == null & !cItem.Selected)
                        {
                            <li><a href="@String.Format("{0}/{1}/{2}", basePath, cItem.Controller, cItem.Action)"><i class="@cItem.Icon"></i> <span>@cItem.Text</span></a></li>
                        }                    
                    }

                @Html.Raw("</ul>");


                @Html.Raw("</li>");
            }

        }
    @Html.Raw("</ul>")
</div>

视图中的实现:

@{Html.RenderAction("GenerateMenu", "Navigation");}

控制器检查当前动作/控制器名称是否与菜单上的一个匹配,如果匹配,则设置selected = true. 在局部视图中,有一些逻辑可以根据父/子关系确定显示结构,如果选择了子,则选择了父。

简而言之,就是这样。我想听听一些评论/其他例子。

于 2013-11-08T21:56:23.600 回答
1

这是一个使用已接受答案中的大部分代码的解决方案,重构为使用 HtmlHelpers 和 TagBuilders 并稍作重命名以适合我的项目。

模型:

public class MenuViewModel
{
    public IList<MenuItemDto> MenuItems; 
}

public class MenuItemDto
{
    public string Text { get; set; }
    public string Action { get; set; }
    public string Controller { get; set; }
    public string IconCssClass { get; set; }
    public bool Active { get; set; }

    public List<MenuItemDto> MenuChildren;
}

控制器:

public ActionResult GenerateMenu()
{
    var viewModel = new MenuViewModel();
    viewModel.MenuItems = //code to build menu model like ElHaix provided in his Controller;

    return PartialView("~/Views/Shared/_Menu.cshtml", viewModel);
}

共享视图:

@using Extensions

<div id="sidebar">
    @Html.Raw("<ul>")

    @foreach (var item in Model.MenuItems)
    {
        // if the menu item does not have     children then it should be clickable
        if (item.MenuChildren == null)
        {
            @Html.LiForMenuItem(item)
        }

        // has children and one of its children     is selected
        if (item.MenuChildren != null)
        {
            if (item.MenuChildren.Any(c => c.    Active) == true)
            {
                <text><li class="submenu active     open">
                </text>
            }
            else
            {
                <text>
                <li class="submenu">
                </text>
            }

            // sub-menu parent
            if (item.MenuChildren != null)
            {
                @Html.HrefForSubMenuItemRoot(    item)
            }

            // children
            <text><ul>
            </text>
            // iterate through children
            foreach (var cItem in item.    MenuChildren)
            {
                if (cItem.MenuChildren == null)
                {
                    @Html.LiForMenuItem(cItem)
                }
            }
            @Html.Raw("</ul>");

        @Html.Raw("</li>");
        }

    }
    @Html.Raw("</ul>")
</div>

Html 助手:

namespace Extensions
{
    public static class MenuExtensions
    {
        public static MvcHtmlString LiForMenuItem(this HtmlHelper htmlHelper, MenuItemDto menuItem)
        {
            var li = new TagBuilder("li");
            AddActiveCssClassToTag(menuItem, li);
            var contentUrl = GenerateContentUrlFromHttpContext(htmlHelper);
            li.InnerHtml = GenerateLinkForMenuItem(menuItem, contentUrl);
            return MvcHtmlString.Create(li.ToString());
        }

        public static MvcHtmlString HrefForSubMenuItemRoot(this HtmlHelper htmlHelper, MenuItemDto menuItem)
        {
            var a = new TagBuilder("a");
            AddActiveCssClassToTag(menuItem, a);
            var contentUrl = GenerateContentUrlFromHttpContext(htmlHelper);
            a.Attributes.Add("href", GenerateUrlForMenuItem(menuItem, contentUrl));
            a.InnerHtml = GenerateInnerHtmlForMenuItem(menuItem);
            return MvcHtmlString.Create(a.ToString());
        }

        private static void AddActiveCssClassToTag(MenuItemDto menuItem, TagBuilder tag)
        {
            if (menuItem.Active)
            {
                tag.AddCssClass("active");
            }
        }

        private static string GenerateContentUrlFromHttpContext(HtmlHelper htmlHelper)
        {
            return UrlHelper.GenerateContentUrl("~/", htmlHelper.ViewContext.HttpContext);
        }

        private static string GenerateLinkForMenuItem(MenuItemDto menuItem, string contentUrl)
        {
            var a = new TagBuilder("a");
            a.Attributes.Add("href", GenerateUrlForMenuItem(menuItem, contentUrl));
            a.InnerHtml = GenerateInnerHtmlForMenuItem(menuItem);
            return a.ToString();
        }

        private static string GenerateInnerHtmlForMenuItem(MenuItemDto menuItem)
        {
            var html = string.Empty;

            //Add <i></i> if there is an IconCssClass present
            var i = new TagBuilder("i");
            if (!String.IsNullOrEmpty(menuItem.IconCssClass))
            {
                i.AddCssClass(menuItem.IconCssClass);
                html += i.ToString();
            }

            //add a span for the text of the menuItem
            var span = new TagBuilder("span");
            span.InnerHtml = menuItem.Text;

            html += span.ToString();

            return html;
        }

        private static string GenerateUrlForMenuItem(MenuItemDto menuItem, string contentUrl)
        {
            var url = contentUrl + menuItem.Controller;
            if (!String.IsNullOrEmpty(menuItem.Action)) url += "/" + menuItem.Action;
            return url;
        }
    }
}
于 2014-01-03T20:38:21.803 回答