【问题标题】:ASP.NET MVC navigation route that changes based on viewed data根据查看的数据更改的 ASP.NET MVC 导航路线
【发布时间】:2010-10-13 03:03:27
【问题描述】:

我正在创建路线位置可以变化的选项卡式导航。用于呈现选项卡的参数应基于当前查看的数据(当它是用户时,可能不是登录用户)。

在示例图像中,这是一个用户。因此,如果我正在查看 Andrew Steele,那么链接应该与 Andrew Steele 相关(Andrew 的摘要、计算机、帐户等)。当我查看布鲁斯汉密尔顿时,链接应该与布鲁斯汉密尔顿相关(布鲁斯的摘要、计算机、帐户等)。

我已经解决了这个问题,方法是在每个 ViewModel 中粘贴必要的参数值,然后将值传递给一个部分以呈现链接;这感觉很笨拙。我不希望将链接参数数据推送到每个 ViewModel 中。将 Html.Action 或 Html.RenderAction 与 ViewData Dictionary 值结合使用似乎是合理的,但我倾向于尽可能避免使用“魔术字符串”。

有没有更好的方法来获取我缺少的视图的参数值?

【问题讨论】:

  • 我认为这里不需要投票
  • @Muhammad 这就是为什么你也有投票权。 :)
  • 您是想为每个人呈现不同的标签,还是只是使用不同参数的同一组标签?
  • @Wyatt 具有不同参数的同一组选项卡。

标签: c# asp.net-mvc asp.net-mvc-2


【解决方案1】:

不久前,我写了一个网站,其中有不同的标签,可以为不同的用户转到不同的地方。我将引导您完成我的解决方案,希望我正确理解了这个问题并且其中的一些帮助。

就在视图中获取数据和从视图中获取数据而言,我确实使用了 ViewDataDictionary。据我所知,这就是您的模型不包含单个简单对象时的用途。为了绕过视图键的“魔术字符串”,我在 ViewDataDictionary 上创建了一堆扩展方法。这样做的缺点是您最终会使用大量额外的方法,但至少您的所有字符串键都被隔离在一个位置。您甚至可以在类中创建常量的额外步骤,但是当只有这个类使用它们时,这似乎是多余的。扩展属性会更好,但是...

/// <summary>
/// Gets the list of tabs to show.
/// </summary>
/// <param name="dictionary"></param>
/// <returns></returns>
public static IList<TabItemDisplay> TabListGet(this ViewDataDictionary dictionary)
{
    IList<TabItemDisplay> result;
    if (dictionary.ContainsKey("TabList"))
        result = dictionary["TabList"] as IList<TabItemDisplay>;
    else
        result = null;
    return result;
}

/// <summary>
/// Sets the list of tabs to show.
/// </summary>
/// <param name="dictionary"></param>
/// <param name="tabList"></param>
/// <returns></returns>
public static IList<TabItemDisplay> TabListSet(this ViewDataDictionary dictionary, IList<TabItemDisplay> tabList)
{
    dictionary["TabList"] = tabList;
    return tabList;
}

您会注意到我有一个明确的视图对象 TabItemDisplay,我将它传递到字典中。这包含传递给 Html.ActionLink 所需的所有值。

public class TabItemDisplay
{
    public string Name { get; set; }
    public string Action { get; set; }
    public string Controller { get; set; }
    public object RouteValues { get; set; }
}

由于这个视图不是页面的主要内容,所以我更喜欢把创建标签项的逻辑,包括目标参数,放到一个ActionFilter中。这使我可以跨不同的操作和控制器重用选项卡创建逻辑。任何包含选项卡部分控件的 View 都会在相应的 Action 或 Controller 上获得 CreatTabAttribute,这很好。

这可能超出了您的需要,但我希望它对您有所帮助。

编辑:刚刚意识到我没有在局部视图中包含它的样子。我实际上有一个 HtmlHelper 扩展,它可以呈现一个更复杂的选项卡,但你明白了。

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<div id="tabs">
<%  
    if (null != ViewData.TabListGet()) {
        foreach(var item in ViewData.TabListGet()) {
%>
    <%= Html.ActionLink(item.Name, item.Action, item.Controller, item.RouteValues, null)%>
<%      
        }
    }
%>
</div>

编辑:添加我使用的 ActionFilter 的简短示例。

public class CreateContentTabsAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var result = filterContext.Result as ViewResultBase;
        if (null == result) return;

        var routeValues = filterContext.RouteData.Values;
        var repository = ObjectFactory.GetInstance<ITabRepository>();
        var context = filterContext.HttpContext;
        var userName = context.User.Identity.Name; // Or get id from Membership.
        var tabs = repository.ReadByUserId(userName);  

        TabItemDisplay defaultTab = null;
        var tabItems = new List<TabItemDisplay>();
        foreach (var tab in tabs)
        {
            var tabItem = new TabItemDisplay 
            { 
                Name = tab.Name,
                Action = "View",
                Controller = "Tab",
                RouteValues = new { key = tab.Key }
            };
            tabItems.Add(tabItem);
        }

        if (context.Request.IsAuthenticated)
        {
            tabItems.Add(new TabItemDisplay
            {
                Name = "Account",
                Action = "ChangePassword",
                Controller = "Account",
                RouteValues = new { siteKey = site.Key }
            });
        }

        result.ViewData.TabListSet(tabItems);
    }
}

这只是从存储库中提取选项卡的一个基本示例(使用 StructureMap 实例化),以及一个简单的检查以查看用户是否已通过身份验证。但是您可以做其他事情,例如从 routeValues 中为正在显示的用户提取请求的用户 ID。

【讨论】:

  • 这看起来很吸引人,您是否使用ActionFilter 装饰每个将呈现带有相关选项卡的视图的操作?
  • 我愿意。如果您不限制 ActionFilter 并且控制器中的所有操作都需要过滤器,则您可以在控制器级别使用它一次。无论如何,结果是让您的标签创建逻辑保持独立,并一目了然地查看哪些控制器/操作正在使用它。
  • 我正在实施您建议的解决方案。您覆盖了哪个ActionFilterAttribute,该方法是什么样的?
  • @ahsteele,在我上面的答案中添加了 ActionFilter 的示例。我取出了一些用于测试目的的代码,但你明白了。
  • 太棒了!我已经覆盖了OnActionExecuted,因为它似乎是最合适的。感谢您更新答案!
【解决方案2】:

您可以为此创建一个显示选项卡首选项的视图模型。如果每个选项卡完全不同,您可以有一个视图模型基类来公开选项卡首选项,并且每个选项卡都可以有自己的视图模型。

【讨论】:

  • 我明白你在说什么。值得庆幸的是,这些选项卡都使用相同的键,所以一个 ViewModel 就足够了。我如何将数据获取到选项卡式 ViewModel 中?
【解决方案3】:

就个人而言,我会使用 MVC 期货中的 RenderAction 或此处的渲染部分调用,具体取决于性能要求和品味。

RenderAction 的优点是您不需要将所有需要的渲染数据传递给委托控制器操作,因为它可以自己进行查找。而部分视图需要您的主页有足够的视图数据来呈现选项卡。无论哪种方式,它都只有两个 for 循环(一个用于选项卡列表,一个用于选项卡渲染)和模型中的一些额外数据。

【讨论】:

  • 是模型中的一些额外数据让我嘴里有酸味。在走这条路时,我同意RenderAction 是两者中更好的,因为它需要更少的“额外”信息,但它仍然可能需要在控制器中进行一些巨大的飞跃才能将必要的识别数据输入模型。跨度>
  • 有趣。我不会称它为额外数据——您确实需要它来呈现页面。您可以轻松地将这些选项卡呈现为页面的一部分,拥有单独的文件更易于管理。 . .
【解决方案4】:

一种解决方案是创建基本控制器并让您的所有控制器都继承自它。在基本控制器中,您可以将必要的值添加到视图模型中

public applicationController:Controller
{
   ViewData["linkvals"] = someValues;
}

public HomeContorller:ApplicationController{}

第二种解决方案是创建一个基本视图模型并让您的所有视图模型继承它。在基本视图模型的构造函数中,您可以创建链接值并将它们分配给您的属性

public baseViewModel
{
   public LinkVal{get;set;}
   public baseViewModel()
   {
      //calculate link vals
   }
}

public ViewModelA:baseViewModel{}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2020-05-14
    相关资源
    最近更新 更多