O que vejo é uma propriedade de layout de string. Mas como posso passar um modelo para layout explicitamente?
Model
está disponível em _Layout
. Estou usando MVC5.
O que vejo é uma propriedade de layout de string. Mas como posso passar um modelo para layout explicitamente?
Model
está disponível em _Layout
. Estou usando MVC5.
Respostas:
Parece que você modelou seus viewmodels um pouco errados se tiver esse problema.
Pessoalmente, eu nunca digitaria uma página de layout. Mas se você quiser fazer isso, você deve ter um modelo de visão base do qual seus outros modelos de visão herdam e digitar seu layout para o modelo de visão base e suas páginas para o modelo específico uma vez.
Exemplo: Controlador:
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;
}
}
Exemplo de topo da página de layout
@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}
Agora você pode referenciar a variável 'viewModel' em sua página de layout com acesso total ao objeto digitado.
Gosto dessa abordagem porque é o controlador que controla o layout, enquanto os modelos de visualização de página individuais permanecem agnósticos de layout.
Notas para MVC Core
IActionFilter
e fazer exatamente o mesmo trabalho em OnActionExecuting
. Coloque MyActionFilter
em seu 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;
}
}
}
isso é muito básico, tudo o que você precisa fazer é criar um modelo de visualização básico e certificar-se de TUDO! e eu quero dizer TODOS! das suas visualizações que usarão aquele layout receberão visualizações que usam aquele modelo básico!
public class SomeViewModel : ViewModelBase
{
public bool ImNotEmpty = true;
}
public class EmptyViewModel : ViewModelBase
{
}
public abstract class ViewModelBase
{
}
no _Layout.cshtml:
@model Models.ViewModelBase
<!DOCTYPE html>
<html>
and so on...
no método Index (por exemplo) no controlador doméstico:
public ActionResult Index()
{
var model = new SomeViewModel()
{
};
return View(model);
}
o Index.cshtml:
@model Models.SomeViewModel
@{
ViewBag.Title = "Title";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
Eu discordo que passar um modelo para _layout é um erro, algumas informações do usuário podem ser passadas e os dados podem ser preenchidos na cadeia de herança dos controladores, portanto, apenas uma implementação é necessária.
obviamente, para fins mais avançados, você deve considerar a criação de um contaxt estático customizado usando injeção e incluir esse namespace de modelo no _Layout.cshtml.
mas para usuários básicos isso fará o truque
Uma solução comum é fazer um modelo de vista base que contenha as propriedades usadas no arquivo de layout e então herdar do modelo base para os modelos usados nas respectivas páginas.
O problema com essa abordagem é que agora você se prendeu ao problema de um modelo só pode herdar de uma outra classe, e talvez sua solução seja tal que você não possa usar herança no modelo que pretendia de qualquer maneira.
Minha solução também começa com um modelo de visualização de base:
public class LayoutModel
{
public LayoutModel(string title)
{
Title = title;
}
public string Title { get;}
}
O que eu uso então é uma versão genérica do LayoutModel que herda do LayoutModel, assim:
public class LayoutModel<T> : LayoutModel
{
public LayoutModel(T pageModel, string title) : base(title)
{
PageModel = pageModel;
}
public T PageModel { get; }
}
Com esta solução, desconectei a necessidade de haver herança entre o modelo de layout e o modelo.
Agora posso ir em frente e usar LayoutModel em Layout.cshtml assim:
@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>
E em uma página, você pode usar o LayoutModel genérico como este:
@model LayoutModel<Customer>
@{
var customer = Model.PageModel;
}
<p>Customer name: @customer.Name</p>
Do seu controlador, você simplesmente retorna um modelo do tipo LayoutModel:
public ActionResult Page()
{
return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}
Por que você simplesmente não adiciona uma nova vista parcial com o próprio controlador específico de i passando o modelo necessário para a vista parcial e, finalmente, renderiza a vista parcial mencionada em seu Layout.cshtml usando RenderPartial ou RenderAction?
Eu uso este método para mostrar as informações do usuário conectado, como nome, foto do perfil e etc.
velha questão, mas apenas para mencionar a solução para desenvolvedores MVC5, você pode usar a Model
mesma propriedade do modo de exibição.
A Model
propriedade na visualização e no layout é associada ao mesmo ViewDataDictionary
objeto, portanto, você não precisa fazer nenhum trabalho extra para passar seu modelo para a página de layout e não precisa declarar @model MyModelName
no layout.
Mas observe que quando você usa @Model.XXX
no layout, o menu de contexto do intelliSense não aparecerá porque o Model
here é um objeto dinâmico exatamente como ViewBag
.
Talvez não seja tecnicamente a maneira correta de lidar com isso, mas a solução mais simples e razoável para mim é apenas fazer uma classe e instanciá-la no layout. É uma exceção única à maneira correta de fazê-lo. Se isso for feito mais do que no layout, você precisa repensar seriamente o que está fazendo e talvez ler mais alguns tutoriais antes de prosseguir em seu projeto.
public class MyLayoutModel {
public User CurrentUser {
get {
.. get the current user ..
}
}
}
então na vista
@{
// Or get if from your DI container
var myLayoutModel = new MyLayoutModel();
}
no núcleo .net você pode até pular isso e usar injeção de dependência.
@inject My.Namespace.IMyLayoutModel myLayoutModel
É uma daquelas áreas meio sombrias. Mas, dadas as alternativas extremamente complicadas que estou vendo aqui, acho que é mais do que uma exceção normal a se fazer em nome da praticidade. Especialmente se você se certificar de mantê-lo simples e certificar-se de que qualquer lógica pesada (eu diria que realmente não deveria haver nenhuma, mas os requisitos são diferentes) está em outra classe / camada a que pertence. É certamente melhor do que poluir TODOS os seus controladores ou modelos por causa de basicamente apenas uma visualização.
Existe outra maneira de arquivá-lo.
Basta implementar a classe BaseController para todos os controladores .
Na BaseController
classe crie um método que retorne uma classe Model como por exemplo.
public MenuPageModel GetTopMenu() { var m = new MenuPageModel(); // populate your model here return m; }
Layout
página você pode chamar esse métodoGetTopMenu()
@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>
Vamos supor que seu modelo seja uma coleção de objetos (ou talvez um único objeto). Para cada objeto no modelo, faça o seguinte.
1) Coloque o objeto que você deseja exibir no ViewBag. Por exemplo:
ViewBag.YourObject = yourObject;
2) Adicione uma instrução using no topo de _Layout.cshtml que contém a definição da classe para seus objetos. Por exemplo:
@using YourApplication.YourClasses;
3) Quando você faz referência a seuObjeto em _Layout, faça a projeção. Você pode aplicar o elenco por causa do que você fez em (2).
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; }
}
Use IContainsMyModel em seu layout.
Resolvido. Regra de interfaces.
Por exemplo
@model IList<Model.User>
@{
Layout="~/Views/Shared/SiteLayout.cshtml";
}
Leia mais sobre a nova diretiva @model