ASP.NET MVC Razor передать модель в макет - PullRequest
86 голосов
/ 11 ноября 2010

Я вижу строковое свойство Layout.Но как я могу явно передать модель макету?

Ответы [ 10 ]

71 голосов
/ 14 февраля 2013
  1. Добавьте свойство к вашему контроллеру (или базовому контроллеру) с именем MainLayoutViewModel (или любым другим) с любым типом, который вы хотели бы использовать.
  2. В конструкторе вашего контроллера (или базового контроллера),создайте экземпляр типа и установите его для свойства.
  3. Установите его в поле ViewData (или ViewBag)
  4. На странице макета приведите это свойство к вашему типу.

Пример: контроллер:

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;
            }
        }
    }
64 голосов
/ 11 ноября 2010

Похоже, что вы смоделировали свои модели представления немного неправильно, если у вас есть эта проблема.

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

24 голосов
/ 31 марта 2015

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

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...

в индексе (например)метод в домашнем контроллере:

    public ActionResult Index()
    {
        var model = new SomeViewModel()
        {
        };
        return View(model);
    }

Index.cshtml:

@model Models.SomeViewModel

@{
  ViewBag.Title = "Title";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="row">

Я не согласен с тем, что передача модели в _layout является ошибкой, некоторая информация о пользователе может быть передана иданные могут быть заполнены в цепочке наследования контроллеров, поэтому требуется только одна реализация.

, очевидно, для более сложных целей следует рассмотреть возможность создания настраиваемого статического контекста с помощью внедрения и включить это пространство имен модели в _Layout.cshtml.

но для обычных пользователей это подойдет

12 голосов
/ 19 декабря 2017

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

Проблема с этим подходом заключается в том, что вы теперь заперты в проблеме модели, которая может наследоваться только от одного другого класса, и, возможно, ваше решение таково, что вы все равно не можете использовать наследование для модели, которую вы намеревались.

Мое решение также начинается с модели базового вида:

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");
}
11 голосов
/ 21 февраля 2015

Почему бы вам просто не добавить новое частичное представление с собственным контроллером i, передав необходимую модель частичному представлению, и, наконец, визуализировать упомянутое частичное представление в вашем Layout.cshtml с помощью RenderPartial или RenderAction?

Я использую этот метод для отображения информации о пользователях, вошедших в систему, таких как имя, изображение профиля и т. Д.

2 голосов
/ 17 октября 2017

старый вопрос, но если упомянуть решение для разработчиков MVC5, вы можете использовать свойство Model так же, как в представлении.

Свойство Model как в виде, так и в макете связано с одним и тем же объектом ViewDataDictionary, поэтому вам не нужно делать никаких дополнительных действий для передачи модели на страницу макета, и вы нев макете нужно объявить @model MyModelName.

Но обратите внимание, что при использовании в макете @Model.XXX контекстное меню intelliSense не появится, поскольку Model здесь представляет собой динамический объект, аналогичный ViewBag.

1 голос
/ 29 июня 2017

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

public class MyLayoutModel {
    public User CurrentUser {
        get {
            .. get the current user ..
        }
    }
}

, затем в представлении

@{
    // Or get if from your DI container
    var myLayoutModel = new MyLayoutModel();
}

в ядре .net вы можете даже пропустить это и использовать инъекцию зависимостей.

@inject My.Namespace.IMyLayoutModel myLayoutModel

Это одна из тех областей, которая немного неясна.Но учитывая чрезвычайно сложные альтернативы, которые я вижу здесь, я думаю, что это более чем нормальное исключение, которое можно сделать во имя практичности.Особенно, если вы позаботитесь о том, чтобы сохранить простоту и убедиться, что любая тяжелая логика (я бы сказал, что их не должно быть, но требования отличаются) относится к другому классу / слою, к которому она принадлежит.Это, безусловно, лучше, чем загрязнение ВСЕХ ваших контроллеров или моделей ради одного лишь вида ...

0 голосов
/ 17 октября 2016

Давайте предположим, что ваша модель представляет собой набор объектов (или, может быть, один объект).Для каждого объекта в модели сделайте следующее.

1) Поместите объект, который вы хотите отобразить в ViewBag.Например:

  ViewBag.YourObject = yourObject;

2) Добавьте оператор using в верхней части _Layout.cshtml, который содержит определение класса для ваших объектов.Например:

@ используя YourApplication.YourClasses;

3) Когда вы ссылаетесь на ваш объект в _Layout, приведите его.Вы можете применить приведение из-за того, что вы сделали в (2).

0 голосов
/ 06 февраля 2015
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 в макете.

решаемая. Правило интерфейсов.

0 голосов
/ 11 ноября 2010

Например

@model IList<Model.User>

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

Подробнее о новой директиве @

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...