Использовать контроллер для частичного просмотра

В приложении корпоративного уровня все (ну, почти) должно быть настраиваемым. Вот почему я создал модель меню и элементов меню, которая позволит администратору создавать несколько меню:

public class Menu
{
    [Key]
    public int ID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public virtual ICollection<MenuItem> MenuItems { get; set; }
}

public class MenuItem
{
    [Key]
    public int ID { get; set; }
    public int? MenuID { get; set; }
    public int? ParentMenuItemID { get; set; }
    public virtual IdentityRole Role { get; set; }
    public string Name { get; set; }
    public string Area { get; set; }
    public string Controller { get; set; }
    public string Action { get; set; }
    public string Url { get; set; }
    public string CssClass { get; set; }
    public string Icon { get; set; }
    public virtual ICollection<MenuItem> MenuItems { get; set; }
    public virtual Menu Menu { get; set; }
}

Я пытаюсь дать им возможность отображать конкретное меню в частичном представлении, которое будет отображаться в представлении макета, и применяется безопасность:

<ul>
    <li>
        <div class="sidebar-toggler hidden-phone"></div>
    </li>
    <li>
        <form class="sidebar-search">
            <div class="input-box">
                <a href="javascript:;" class="remove"></a>
                <input type="text" placeholder="Search..." />
                <input type="button" class="submit" value=" " />
            </div>
        </form>
    </li>
    @foreach (var m in Model.MenuItems)
    {
        if (m.Role == null || User.IsInRole(m.Role.Name))
        {
            <li class="@(m.MenuItems != null && m.MenuItems.Count > 0 ? "has-sub " : "") @(ViewContext.RouteData.Values["Controller"].ToString() == m.Controller || (ViewContext.RouteData.DataTokens["area"] != null && ViewContext.RouteData.DataTokens["area"].ToString() == m.Area) ? "active " : " ") ">
                <a href="@Url.Action(m.Action, m.Controller, new { area = (string.IsNullOrEmpty(m.Area) ? "" : m.Area) }, null)">
                    @if (!string.IsNullOrEmpty(m.Icon))
                    {
                        <i class="@m.Icon"></i>
                    }
                    <span class="title">@m.Name</span>
                    @if (ViewContext.RouteData.Values["Controller"].ToString() == m.Controller || (ViewContext.RouteData.DataTokens["area"] != null && ViewContext.RouteData.DataTokens["area"].ToString() == m.Area))
                    {
                        <span class="selected"></span>
                    }
                </a>
                @if (m.MenuItems != null && m.MenuItems.Count > 0)
                {
                    <ul class="sub">
                        @foreach (var mi in m.MenuItems)
                        {
                            if (mi.Role == null || User.IsInRole(mi.Role.Name))
                            {
                                <li class="@(ViewBag.Title == mi.Name ? "active" : "")">
                                    <a href="@Url.Action(mi.Action, mi.Controller, new { area = mi.Area })">@mi.Name</a>
                                </li>
                            }
                        }
                    </ul>
                }
            </li>
        }
    }
</ul>

Чтобы протестировать мой текущий код, я жестко запрограммировал идентификатор меню в представлении макета:

<div class="page-container row-fluid">
    <div class="page-sidebar nav-collapse collapse">
        @Html.Partial("_LeftMenu", new MyAppContext().Menus.Find(1))
    </div>
    <div class="page-content">
        @RenderBody()
    </div>
</div>

Теперь возникла пара проблем. Во-первых, мне нужно передать идентификатор меню партиалу, чтобы он отображал правильное меню. Используя метод частичного просмотра, я не знаю, как достичь этой цели. Во-вторых, когда я применил защиту к загружаемому меню, пункты меню отображаются в обратном порядке. Таким образом, наличие контроллера некоторого типа для выполнения логики данных для меню и макета должно решить эту проблему.

Итак, у кого-нибудь есть какие-либо предложения относительно того, в каком направлении мне нужно двигаться? Я начинаю думать, что моя попытка использовать частичное представление меню (будучи динамическим) — плохая идея?


person clockwiseq    schedule 29.10.2014    source источник


Ответы (1)


Вместо прямого вызова @Html.Partial вы можете иметь контроллер с методом действия и использовать @Html.RenderAction вместо метода @Html.Partial. В методе действия вы можете передать идентификатор, а также применить необходимую логику безопасности, а в представлении просто перебрать коллекцию меню и показать необходимые меню. Это также сохранит ваш пользовательский интерфейс в чистоте.

person Jags    schedule 29.10.2014
comment
Я думаю, что у меня есть это: @{ Html.RenderAction("UserMenuAction", "Menu", new { area = "Admin", id = 1 }); }, но теперь у меня есть проблема. Я выбирал активный пункт меню на основе контроллера и/или области страницы. Теперь меню всегда возвращает область администратора, потому что это используемый контроллер (ViewContext.RouteData.Values["Controller"].ToString() и ViewContext.RouteData.DataTokens["area"]). Какие-либо предложения? - person clockwiseq; 29.10.2014
comment
Ну, я не думаю, что вы можете использовать эту логику для выбора пункта меню. Возможно, вы захотите добавить состояние выбранного меню в сеанс или добавить в ViewBag и использовать его для отображения активного пункта меню. Есть и другие лучшие способы делать это, но это зависит от того, как организован конкретный проект. - person Jags; 29.10.2014
comment
В итоге я передал Area, Controller и Action в UserMenuAction из _Layout, и все отлично работает! Спасибо за толчок в правильном направлении. - person clockwiseq; 29.10.2014