Бритвові вкладені макети з каскадними розділами


80

У мене є сайт MVC3, який використовує Razor як механізм перегляду. Я хочу, щоб мій сайт можна було скинути. Більшість можливих скінів досить подібні, щоб їх можна було отримати із спільного головного макета.

Тому я розглядаю такий дизайн:

Діаграма запланованого вигляду

Тим НЕ менше, я хотів би мати можливість викликати RenderSectionв нижньому шарі, _Common.cshtmlі мати його винести розділ, визначений у верхньому шарі, Detail.cshtml. Це не працює: RenderSectionочевидно, рендерує лише ті розділи, які визначені наступним шаром вгору.

Звичайно, я можу визначити кожен розділ у кожній обробці. Наприклад, якщо _Commonпотрібно викликати RenderSection("hd")розділ, визначений у Detail, я просто розміщую це в кожному, _Skinі воно працює:

@section hd {
    @RenderSection("hd")
}

Це призводить до деякого дублювання коду (оскільки кожна обшивка тепер повинна мати цей самий розділ) і, як правило, відчуває себе безладно. Я все ще новачок у Razor, і, здається, мені може не вистачити чогось очевидного.

Під час налагодження я бачу повний список визначених розділів у WebViewPage.SectionWritersStack. Якби я міг просто сказати RenderSection переглянути весь список, перш ніж відмовлятися, він знайшов би потрібний мені розділ. На жаль, SectionWritersStack є загальнодоступною.

Як варіант, якщо я міг отримати доступ до ієрархії сторінок макета та спробувати виконати RenderSection у кожному різному контексті, я міг би знайти потрібний розділ. Я, мабуть, чогось пропускаю, але я не бачу способу цього зробити.

Чи є якийсь спосіб досягнення цієї мети, крім методу, який я вже окреслив?

Відповіді:


35

Насправді це сьогодні неможливо за допомогою загальнодоступного API (крім використання підходу перевизначення розділу). Можливо, вам пощастить із використанням приватних роздумів, але це, звичайно, крихкий підхід. Ми розглянемо спрощення цього сценарію в наступній версії Razor.

Тим часом, ось кілька дописів у блозі, які я написав на цю тему:


3
Дякую за відповідь, я грав із вкладеними розділами, використовуючи такий синтаксис (як зазначено вище): @section hd {@RenderSection ("hd")} ... який насправді працює для мене і схоже на те, що я можу відтворити наявні вкладені сторінки . Думаю, я трохи неправильно зрозумів питання і думав, що це не спрацює.
Марк Редман

2
І питання, і відповідь дуже допомогли, і я теж погоджуюсь, що це слід спростити в наступній версії Razor. І ви також повинні дозволити можливість часткових переглядів також реалізувати розділи, що наразі не підтримується.
кобила

1
@Shrike Я не думаю, що в цій області щось змінилося. Ви можете ввести запити на нові функції на сайті UserVoice або помилок на CodePlex
marcind

1
@marcind Погляньте на мою відповідь. Я думаю, що про це просив ОП. Правда?
Alireza Noori

1
MVC 5 вийшов. Будь-яке оновлення? Алірзеа сказав, що знайшов рішення, але, схоже, воно не відповідає проблемі ОП, оскільки воно взагалі не посилається на розділи.
Снексе

17
@helper ForwardSection( string section )
{
   if (IsSectionDefined(section))
   {
       DefineSection(section, () => Write(RenderSection(section)));
   }
}

Чи зробить це роботу?


Ви використовуєте це в проміжному шарі? Приблизно те саме, що цей клас розширень ? Якщо так, це швидше зручність при повторному оголошенні розділу, а не при вирішенні проблеми, правда? Просто переконуюсь, що я розумію, оскільки я спізнився на цю дискусію.
drzaus

Для мене це було єдиним рішенням, яке спрацювало. Якщо розділ умовно відображається в базовому макеті, MVC видасть помилку виконання, якщо цей розділ умовно не визначений (як цей) у проміжному шарі. Дякую @Randy!
Майкл

Чи можна переслати всі розділи, які визначені на даний момент?
nvirth,

4

Я не впевнений, що це можливо в MVC 3, але в MVC 5 я можу успішно зробити це, використовуючи такий трюк:

В ~/Views/Shared/_Common.cshtmlзапису ваш загальний HTML - код , як:

<!DOCTYPE html>
<html lang="fa">
<head>
    <title>Skinnable - @ViewBag.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>

В ~/Views/_ViewStart.cshtml:

@{
    Layout = "~/Views/Shared/_Common.cshtml";
}

Тепер все, що вам потрібно зробити, це використовувати _Common.cshtmla Layoutдля всіх скінів. Наприклад, у ~/Views/Shared/Skin1.cshtml:

@{
    Layout = "~/Views/Shared/_Common.cshtml";
}

<p>Something specific to Skin1</p>

@RenderBody()

Тепер ви можете встановити обкладинку як ваш макет у контролері або поданні відповідно до ваших критеріїв. Наприклад:

    public ActionResult Index()
    {
        //....
        if (user.SelectedSkin == Skins.Skin1)
            return View("ViewName", "Skin1", model);
    }

Якщо ви запускаєте наведений вище код, ви повинні отримати сторінку HTML із вмістом Skin1.cshtmlі_Common.cshtml

Коротше кажучи, ви встановите макет для сторінки (макета).


У мене були проблеми з цим підходом, оскільки розділи не були видимими. Я знайшов рішення за адресою blogs.msdn.microsoft.com/marcinon/2010/12/15/…
Spikolynn

1

Не впевнений, чи допоможе вам це, але я написав кілька методів розширення, щоб допомогти розділити розділи з частин, які також повинні працювати для вкладених макетів.

Введення вмісту в конкретні розділи з часткового подання ASP.NET MVC 3 за допомогою механізму Razor View

Заявити в дочірньому макеті / поданні / частково

@using (Html.Delayed()) {
    <b>show me multiple times, @Model.Whatever</b>
}

Візуалізувати в будь-якому з батьків

@Html.RenderDelayed();

Див. Посилання для відповіді, щоб дізнатись більше випадків використання, наприклад, лише надання одного затриманого блоку, навіть якщо він оголошений у повторюваному поданні, візуалізація конкретних відкладених блоків тощо

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.