Я вижу строковое свойство Layout. Но как я могу явно передать модель макету?
ASP.NET MVC Razor передает модель в макет
Ответы (12)
Похоже, вы немного неправильно смоделировали свои модели просмотра, если у вас есть эта проблема.
Лично я бы никогда не печатал страницу макета. Но если вы хотите сделать это, у вас должна быть базовая модель просмотра, от которой наследуются другие ваши модели просмотра, и введите свой макет в базовую модель просмотра, а вы просматриваете конкретный раз.
- Добавьте свойство в свой контроллер (или базовый контроллер) с именем MainLayoutViewModel (или что-то еще) с любым типом, который вы хотели бы использовать.
- В конструкторе вашего контроллера (или базового контроллера) создайте экземпляр типа и установите его в свойство.
- Установите его в поле ViewData (или ViewBag)
- На странице макета приведите это свойство к вашему типу.
Пример: Контроллер:
public class MyController : Controller
{
public MainLayoutViewModel MainLayoutViewModel { get; set; }
public MyController()
{
this.MainLayoutViewModel = new MainLayoutViewModel();//has property PageTitle
this.MainLayoutViewModel.PageTitle = "my title";
this.ViewData["MainLayoutViewModel"] = this.MainLayoutViewModel;
}
}
Пример верхней части страницы макета
@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}
Теперь вы можете ссылаться на переменную viewModel на странице макета с полным доступом к типизированному объекту.
Мне нравится этот подход, потому что это контроллер, который управляет макетом, в то время как отдельные модели просмотра страниц остаются независимыми от макета.
Примечания для MVC Core
Mvc Core, по-видимому, удаляет содержимое ViewData/ViewBag при первом вызове каждого действия. Это означает, что назначение ViewData в конструкторе не работает. Что действительно работает, так это использование
IActionFilter и выполнение той же самой работы в OnActionExecuting. Поместите MyActionFilter на свой MyController.
public class MyActionFilter: Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var myController= context.Controller as MyController;
if (myController!= null)
{
myController.Layout = new MainLayoutViewModel
{
};
myController.ViewBag.MainLayoutViewModel= myController.Layout;
}
}
}
Распространенным решением является создание базовой модели представления, которая содержит свойства, используемые в файле макета, а затем наследование от базовой модели моделям, используемым на соответствующих страницах.
Проблема с этим подходом заключается в том, что теперь вы заперлись в проблеме модели, которая может наследоваться только от одного другого класса, и, возможно, ваше решение таково, что вы все равно не можете использовать наследование в модели, которую вы планировали.
Мое решение также начинается с базовой модели представления:
public class LayoutModel
{
public LayoutModel(string title)
{
Title = title;
}
public string Title { get;}
}
Затем я использую общую версию LayoutModel, которая наследуется от LayoutModel, например:
public class LayoutModel<T> : LayoutModel
{
public LayoutModel(T pageModel, string title) : base(title)
{
PageModel = pageModel;
}
public T PageModel { get; }
}
С помощью этого решения я отключил необходимость наследования между моделью макета и моделью.
Итак, теперь я могу использовать LayoutModel в Layout.cshtml следующим образом:
@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>
И на странице вы можете использовать общий LayoutModel следующим образом:
@model LayoutModel<Customer>
@{
var customer = Model.PageModel;
}
<p>Customer name: @customer.Name</p>
Из вашего контроллера вы просто возвращаете модель типа LayoutModel:
public ActionResult Page()
{
return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}
это довольно простой материал, все, что вам нужно сделать, это создать базовую модель представления и убедиться, что ВСЕ! и я имею в виду ВСЕ! ваших представлений, которые когда-либо будут использовать этот макет, получат представления, использующие эту базовую модель!
public class SomeViewModel : ViewModelBase
{
public bool ImNotEmpty = true;
}
public class EmptyViewModel : ViewModelBase
{
}
public abstract class ViewModelBase
{
}
в _Layout.cshtml:
@model Models.ViewModelBase
<!DOCTYPE html>
<html>
and so on...
в методе Index (например) в домашнем контроллере:
public ActionResult Index()
{
var model = new SomeViewModel()
{
};
return View(model);
}
Индекс.cshtml:
@model Models.SomeViewModel
@{
ViewBag.Title = "Title";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
я не согласен с тем, что передача модели в _layout является ошибкой, некоторая информация о пользователе может быть передана, а данные могут быть заполнены в цепочке наследования контроллеров, поэтому требуется только одна реализация.
очевидно, для более продвинутых целей вам следует подумать о создании собственного статического контакта с использованием инъекции и включить это пространство имен модели в _Layout.cshtml.
но для обычных пользователей это поможет
Почему бы вам просто не добавить новый частичный вид с собственным конкретным контроллером i, передающим требуемую модель частичному представлению, и, наконец, визуализировать упомянутый частичный вид в вашем Layout.cshtml с помощью RenderPartial или RenderAction ?
Я использую этот метод для отображения информации о вошедшем в систему пользователе, такой как имя, изображение профиля и т. Д.
старый вопрос, но просто чтобы упомянуть решение для разработчиков MVC5, вы можете использовать свойство Model так же, как и в представлении.
Свойство Model как в представлении, так и в макете связано с одним и тем же объектом ViewDataDictionary, поэтому вам не нужно выполнять дополнительную работу, чтобы передать вашу модель на страницу макета, и вам не нужно объявлять @model MyModelName в макете.
Но обратите внимание, что когда вы используете @Model.XXX в макете, контекстное меню IntelliSense не отображается, потому что Model здесь является динамическим объектом, как и ViewBag.
Возможно, технически это не правильный способ справиться с этим, но самое простое и наиболее разумное решение для меня - просто создать класс и создать его экземпляр в макете. Это одноразовое исключение из правильных способов сделать это. Если это сделано больше, чем в макете, вам нужно серьезно переосмыслить то, что вы делаете, и, возможно, прочитать еще несколько руководств, прежде чем двигаться дальше в своем проекте.
public class MyLayoutModel {
public User CurrentUser {
get {
.. get the current user ..
}
}
}
тогда в представлении
@{
// Or get if from your DI container
var myLayoutModel = new MyLayoutModel();
}
в .net core вы даже можете пропустить это и использовать внедрение зависимостей.
@inject My.Namespace.IMyLayoutModel myLayoutModel
Это одна из тех областей, которая является своего рода теневой. Но, учитывая чрезвычайно сложные альтернативы, которые я вижу здесь, я думаю, что это более чем нормальное исключение, сделанное во имя практичности. Особенно, если вы позаботитесь о том, чтобы все было просто, и убедитесь, что любая сложная логика (я бы сказал, что ее действительно не должно быть, но требования различаются) находится в другом классе/слое, которому она принадлежит. Это, безусловно, лучше, чем загрязнять ВСЕ ваши контроллеры или модели ради одного представления.
Есть еще один способ заархивировать.
Просто реализуйте класс BaseController для всех контроллеров.
В классе
BaseControllerсоздайте метод, который возвращает класс модели, например.
public MenuPageModel GetTopMenu() { var m = new MenuPageModel(); // populate your model here return m; }
- И на странице
Layoutвы можете вызвать этот методGetTopMenu()
@using GJob.Controllers <header class="header-wrapper border-bottom border-secondary"> <div class="sticky-header" id="appTopMenu"> @{ var menuPageModel = ((BaseController)this.ViewContext.Controller).GetTopMenu(); } @Html.Partial("_TopMainMenu", menuPageModel) </div> </header>
Предположим, что ваша модель представляет собой набор объектов (или, может быть, один объект). Для каждого объекта в модели выполните следующие действия.
1) Поместите объект, который вы хотите отобразить, в ViewBag. Например:
ViewBag.YourObject = yourObject;
2) Добавьте оператор using вверху файла _Layout.cshtml, который содержит определение класса для ваших объектов. Например:
@using YourApplication.YourClasses;
3) Когда вы ссылаетесь на свой объект в _Layout, приведите его. Вы можете применить приведение из-за того, что вы сделали в (2).
Только сверху, но простая реализация
Индексная страница
@model CMS.Models.IndexViewModel
@{
ViewBag.PageModel = Model;
}
Макет страницы
@{
var Model = (CMS.Models.IndexViewModel)ViewBag.PageModel;
}
public interface IContainsMyModel
{
ViewModel Model { get; }
}
public class ViewModel : IContainsMyModel
{
public string MyProperty { set; get; }
public ViewModel Model { get { return this; } }
}
public class Composition : IContainsMyModel
{
public ViewModel ViewModel { get; set; }
}
Используйте IContainsMyModel в макете.
Решено. Правило интерфейсов.
Например
@model IList<Model.User>
@{
Layout="~/Views/Shared/SiteLayout.cshtml";
}
Узнайте больше о новом @model директива
Modelдоступен в_Layout. Я использую MVC5. - person toddmo   schedule 28.08.2016