Bezdepbonusu7Программирование: статьи и ответы

Вопрос

ASP.NET MVC Razor передает модель в макет

Автор вопроса: SiberianGuyОтветов: 12

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

  • У меня есть несколько страниц с другой моделью, но с одинаковым макетом SiberianGuy 11.11.2010
  • Этот вопрос stackoverflow, кажется, отвечает на то, что вы спрашиваете: stackoverflow.com/questions/13225315/ Paul 28.07.2014
  • У меня нет этой проблемы. Model доступен в _Layout. Я использую MVC5. toddmo 28.08.2016

razor

Ответы

Ответ 1 · рейтинг 70 · Mattias Jakobsson

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

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

Ответ 2 · рейтинг 85 · BlackjacketMack

  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;
            }
        }
    }

Ответ 3 · рейтинг 35 · Oskar Sjöberg

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

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

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

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");
}

Ответ 4 · рейтинг 30 · Yakir Manor

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

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.

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

Ответ 5 · рейтинг 11 · Arya Sh

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

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

Ответ 6 · рейтинг 4 · LazZiya

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

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

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

Ответ 7 · рейтинг 3 · computrius

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

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

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

Ответ 8 · рейтинг 2 · DmitryBoyko

Есть еще один способ заархивировать.

  1. Просто реализуйте класс BaseController для всех контроллеров.

  2. В классе BaseController создайте метод, который возвращает класс модели, например.

public MenuPageModel GetTopMenu()
{

var m = new MenuPageModel();
// populate your model here
return m;

}
  1. И на странице 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>

Ответ 9 · HappyPawn8

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

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

  ViewBag.YourObject = yourObject;

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

@using YourApplication.YourClasses;

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

Ответ 10 · Arun Prasad E S

Только сверху, но простая реализация

Индексная страница

@model CMS.Models.IndexViewModel

@{
    ViewBag.PageModel = Model;
}

Макет страницы

@{
    var Model = (CMS.Models.IndexViewModel)ViewBag.PageModel;
}

Ответ 11 · рейтинг -2 · Will

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 в макете.

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

Ответ 12 · рейтинг -6 · Martin Fabik

Например

@model IList<Model.User>

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

Узнайте больше о новом @model директива

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы