【问题标题】:Razor code to programmatically hide global menu items?剃刀代码以编程方式隐藏全局菜单项?
【发布时间】:2012-12-11 22:35:10
【问题描述】:

我是一位经验丰富的 .NET 程序员,但对整个 Web 编程这件事我还是新手。我的 ASP.NET MVC 网站有一个全局布局,其中包含一些我想在控制器代码动态检测到的条件下隐藏的内容(页面顶部的菜单链接)。

我的倾向 - 使用我迄今为止所了解的工具的简单方法 - 将 Boolean HideGlobal 值推入 ViewBag,并将全局标记放在我想要隐藏的 _Layout.cshtml 中@if (ViewBag.HideGlobal){} 块。

我只是想知道这是否是“正确”的方法,还是我应该使用一些 Razor 魔法,原因我还不明白?

【问题讨论】:

  • 根据您确定显示或隐藏项目的方式,您可能希望在操作过滤器(控制器外部)中设置 ViewData 项目。
  • 如果您将 HideGlobal 放入视图包中,则需要在每个操作中都这样做。
  • @Omar 这不是真的ActionFilter 意味着你可以在全球范围内做到这一点
  • @Lavinski - 我指的是 Bob 的方法。
  • @Omar 对,在这种情况下是的

标签: asp.net-mvc razor


【解决方案1】:

我不喜欢在动作返回的视图之外使用动作的视图模型。在这种情况下使用基本视图模型感觉非常笨拙。

我相信只使用包含指定全局菜单应如何显示的逻辑的单独(子)操作更清晰、更明显。此操作返回全局菜单视图。从您的布局页面调用该操作。

或者您可以为确定菜单状态的整个标题创建一个操作,或者执行 if/else 以呈现全局菜单的部分视图。

下面的示例封装了标题/全局菜单的需求,并提供了一种面向未来的更改标题/菜单的方法,同时对代码基础架构(基本视图模型)的影响最小。

~/Controllers/LayoutController.cs

public class LayoutController : Controller
{
    [ChildActionOnly]
    public ActionResult Header()
    {
        var model = new HeaderViewModel();
        model.ShowGlobalMenu = ShowGobalMenu();

        return View(model);
    }
}

~/Views/Layout/Header.cshtml

@model HeaderViewModel
@{
    Layout = "";
}

<header>
    <a href="/">Home</a>

    @if(Model.ShowGlobalMenu)
    {
        <ul>
            <li><a href="#">Link</a></li>
            <li><a href="#">Link</a></li>
            <li><a href="#">Link</a></li>
            <li><a href="#">Link</a></li>
        </ul>
    }
</header>

~/Views/Shared/_Layout.cshtml

<html>
    <body>
        @Html.Action("Header", "Layout")

        <p>Stuff</p>
    </body>
</body>

【讨论】:

  • 非常感谢大家,尤其是 Omar 提供了使用子动作的清晰简洁的示例(我遇到过但从未深入研究过如何使用)。 FWIW,我决定使用每个人的一些建议。我的大多数页面都有一个静态布局,要么总是隐藏菜单,要么不想隐藏菜单,但一个页面需要即时做出决定。我将使用操作过滤器属性来装饰页面,该属性设置或清除 ViewBag 值,这些值告诉子操作如何创建标题布局。
【解决方案2】:

您所描述的(将 bool 放入 ViewBag 中)可以正常工作。但就个人而言,我喜欢强类型模型体验,所以当我想要拥有像您在主/布局页面中描述的那样的 UI 逻辑时(几乎总是如此),我更喜欢将这些标志放入基本模型中我的其他模型继承自。

public class BaseModel
{
     public bool HideGlobal { get; set; }
}

在 _Layout.cshtml 页面的顶部,我指定我期待一个 BaseModel:

@model Company.Project.BaseModel

当然,其他视图可能需要其他模型类型,但如果它们使用此布局,则这些模型类型应派生自 BaseModel。

最后,当我想检查标志时,而不是在 ViewBag 中有一个神秘的、未记录的字段,我拥有了模型中可爱的智能感知和感觉良好的强类型成员:

@if (!Model.HideGlobal)
{
    <div>...</div>
}

编辑:我可能应该补充一点,通常还有一个基本控制器,其工作是填充 BaseModel 中的字段。我的通常是这样的:

public class BaseController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var result = filterContext.Result as ViewResultBase;
        if (result != null)
        {
            var baseModel = result.Model as BaseModel;
            if (baseModel != null)
            {
                //Set HideGlobal and other BaseModel properties here
            }
        }
    }
}

这里的第一个答案,温柔:-)

【讨论】:

  • 一个 .net 急需 mixins 的例子...基础模型中的菜单感觉很勉强
  • 这种类型的东西通常基于用户权限,这在动作过滤器而不是基本控制器中会更好。是的,您必须使用 ViewData 或 ViewBag 但它实际上不是视图的一部分,它在 _layout.cshtml 中更高
  • 经过一番思考,对于这个特定的菜单示例,我可能更喜欢 Omar 的回答,其中有一个子动作会使一切都比我在这里更整洁。尽管就我个人而言,我确实喜欢其他场景的基本模型,例如广告数据,它经常影响布局的几个部分,这些部分协同工作并且不是直观隔离的。
  • 好的...如果我使用强类型的 BaseModel 方法,那么每个视图都需要有一个 @model 指令,其参数是 BaseModel 或其子类。这意味着我不能只制作要显示的项目列表并提供视图“@model IEnumerable”。我需要为遇到的每个场景创建一个从 BaseModel 继承的模型。 OTOH,如果我使用 ViewBag,那么我可以测试 (ViewBag.HideGlobal == null || !ViewBag.HideGlobal),除非我需要,否则我不需要在控制器代码中设置 ViewBag.HideGlobal。
  • @Bob.at.SBS,对于我的模型非常简单的情况,例如 IEnumerable 而我不想费心创建自己的模型类,我有一个BaseModel 的通用版本,BaseModel,具有名为“数据”的 T 类型属性。这让我可以享受 BaseModel 方法的好处,而不必为每个视图创建单独的模型类。但是,我通常避免使用该类,因为我发现我几乎总是需要一些其他数据,我宁愿从一个合适的模型类开始。
【解决方案3】:

这种类型的东西通常基于用户权限,这在动作过滤器而不是基本控制器中会更好。

是的,您必须使用 ViewDataViewBag,但它实际上不是视图/模型的一部分,它在您的 _layout.cshtml 中更高

动作过滤器

public class UserAccessAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting( ActionExecutingContext filterContext ) {
        var hasAccessToMenu = /* Code */;

        filterContext.Controller.ViewData["GlobalVisible"] = hasAccessToMenu;
    }
}

查看 (_layout.cshtml)

@if(ViewData["GlobalVisible"])
{
    <ul>
        <li><a href="#">Link</a></li>
        <li><a href="#">Link</a></li>
        <li><a href="#">Link</a></li>
        <li><a href="#">Link</a></li>
    </ul>
}

这为您提供了将这个逻辑从控制器中分离出来的优势,因为它是全局的,这一点很重要。

另外,我在示例中使用了 ViewData,但使用 ViewBag 也一样好。

【讨论】:

  • 实际上,在我的情况下,我需要隐藏全局内容实际上是由控制器中的业务逻辑决定的。我的应用程序是一个美化的面试,一个人设置面试(称为第 1 步),然后将计算机交给接受面试的人(称为第 2 步)。在第 2 步中,我不希望电脑前的人能够离开应用程序,因此当面试控制器确定面试已进行到第 2 步时,我想隐藏菜单。
  • @Bob.at.SBS 那么你可能想更新你的问题,或者问一个更具体的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
相关资源
最近更新 更多