Quello che vedo è una proprietà Layout di stringa. Ma come posso passare esplicitamente un modello al layout?
Model
è disponibile in _Layout
. Sto usando MVC5.
Quello che vedo è una proprietà Layout di stringa. Ma come posso passare esplicitamente un modello al layout?
Model
è disponibile in _Layout
. Sto usando MVC5.
Risposte:
Sembra che tu abbia modellato i tuoi viewmodels un po 'in modo sbagliato se hai questo problema.
Personalmente non scriverei mai una pagina di layout. Ma se vuoi farlo, dovresti avere un viewmodel di base da cui ereditano gli altri tuoi viewmodel e digita il tuo layout nel viewmodel di base e le pagine nello specifico una volta.
Esempio: Controller:
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;
}
}
Esempio di inizio pagina di layout
@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}
Ora puoi fare riferimento alla variabile "viewModel" nella pagina di layout con accesso completo all'oggetto digitato.
Mi piace questo approccio perché è il controller che controlla il layout, mentre i singoli modelli di visualizzazione della pagina rimangono indipendenti dal layout.
Note per MVC Core
IActionFilter
e fare lo stesso identico lavoro in OnActionExecuting
. Metti MyActionFilter
il tuo 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;
}
}
}
questa è roba piuttosto semplice, tutto ciò che devi fare è creare un modello di visualizzazione di base e assicurarti che TUTTO! e intendo TUTTI! delle tue visualizzazioni che utilizzeranno mai quel layout riceveranno visualizzazioni che utilizzano quel modello di base!
public class SomeViewModel : ViewModelBase
{
public bool ImNotEmpty = true;
}
public class EmptyViewModel : ViewModelBase
{
}
public abstract class ViewModelBase
{
}
in _Layout.cshtml:
@model Models.ViewModelBase
<!DOCTYPE html>
<html>
and so on...
nel metodo Index (ad esempio) nel controller home:
public ActionResult Index()
{
var model = new SomeViewModel()
{
};
return View(model);
}
il Index.cshtml:
@model Models.SomeViewModel
@{
ViewBag.Title = "Title";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
Non sono d'accordo sul fatto che il passaggio di un modello a _layout sia un errore, alcune informazioni utente possono essere passate ei dati possono essere popolati nella catena di ereditarietà del controller, quindi è necessaria solo un'implementazione.
ovviamente per scopi più avanzati dovresti prendere in considerazione la creazione di un contenuto statico personalizzato utilizzando l'iniezione e includere lo spazio dei nomi del modello in _Layout.cshtml.
ma per gli utenti di base questo farà il trucco
Una soluzione comune è quella di creare un modello di visualizzazione di base che contenga le proprietà utilizzate nel file di layout e quindi ereditare dal modello di base ai modelli utilizzati nelle rispettive pagine.
Il problema con questo approccio è che ora ti sei bloccato nel problema di un modello che può ereditare solo da un'altra classe, e forse la tua soluzione è tale che non puoi comunque usare l'ereditarietà sul modello che intendi.
La mia soluzione inizia anche con un modello di visualizzazione di base:
public class LayoutModel
{
public LayoutModel(string title)
{
Title = title;
}
public string Title { get;}
}
Quello che uso quindi è una versione generica del LayoutModel che eredita dal LayoutModel, in questo modo:
public class LayoutModel<T> : LayoutModel
{
public LayoutModel(T pageModel, string title) : base(title)
{
PageModel = pageModel;
}
public T PageModel { get; }
}
Con questa soluzione ho scollegato la necessità di avere l'ereditarietà tra il modello di layout e il modello.
Quindi ora posso andare avanti e utilizzare LayoutModel in Layout.cshtml in questo modo:
@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>
E su una pagina puoi usare il LayoutModel generico in questo modo:
@model LayoutModel<Customer>
@{
var customer = Model.PageModel;
}
<p>Customer name: @customer.Name</p>
Dal tuo controller restituisci semplicemente un modello di tipo LayoutModel:
public ActionResult Page()
{
return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}
Perché non aggiungi semplicemente una nuova vista parziale con il controller specifico di i che passa il modello richiesto alla vista parziale e infine il rendering della vista parziale menzionata sul tuo Layout.cshtml usando RenderPartial o RenderAction?
Uso questo metodo per mostrare le informazioni dell'utente connesso come nome, immagine del profilo e così via.
vecchia domanda ma solo per citare la soluzione per gli sviluppatori MVC5, è possibile utilizzare la Model
proprietà come in vista.
La Model
proprietà sia nella vista che nel layout è associata allo stesso ViewDataDictionary
oggetto, quindi non devi fare alcun lavoro extra per passare il tuo modello alla pagina del layout e non devi dichiararlo @model MyModelName
nel layout.
Notare però che quando si utilizza @Model.XXX
nel layout il menu contestuale di intelliSense non verrà visualizzato perché Model
qui è un oggetto dinamico proprio come ViewBag
.
Forse non è tecnicamente il modo corretto per gestirlo, ma la soluzione più semplice e ragionevole per me è semplicemente creare una classe e istanziarla nel layout. È un'eccezione una tantum al modo altrimenti corretto di farlo. Se questo viene fatto più che nel layout, è necessario ripensare seriamente a ciò che si sta facendo e magari leggere qualche altro tutorial prima di procedere ulteriormente nel progetto.
public class MyLayoutModel {
public User CurrentUser {
get {
.. get the current user ..
}
}
}
poi nella vista
@{
// Or get if from your DI container
var myLayoutModel = new MyLayoutModel();
}
in .net core puoi persino ignorarlo e utilizzare l'inserimento delle dipendenze.
@inject My.Namespace.IMyLayoutModel myLayoutModel
È una di quelle aree che è un po 'ombreggiata. Ma date le alternative estremamente complicate che vedo qui, penso che sia più che un'eccezione accettabile da fare in nome della praticità. Soprattutto se ti assicuri di mantenerlo semplice e ti assicuri che qualsiasi logica pesante (direi che in realtà non dovrebbe essercene, ma i requisiti differiscono) si trova in un'altra classe / livello a cui appartiene. È sicuramente meglio che inquinare TUTTI i tuoi controller o modelli per il gusto di fondamentalmente una sola vista.
C'è un altro modo per archiviarlo.
Basta implementare la classe BaseController per tutti i controller .
Nella BaseController
classe creare un metodo che restituisca una classe Model come per esempio.
public MenuPageModel GetTopMenu() { var m = new MenuPageModel(); // populate your model here return m; }
Layout
pagina puoi chiamare quel metodoGetTopMenu()
@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>
Supponiamo che il tuo modello sia una raccolta di oggetti (o forse un singolo oggetto). Per ogni oggetto nel modello eseguire le seguenti operazioni.
1) Metti l'oggetto che vuoi mostrare nel ViewBag. Per esempio:
ViewBag.YourObject = yourObject;
2) Aggiungi un'istruzione using all'inizio di _Layout.cshtml che contiene la definizione della classe per i tuoi oggetti. Per esempio:
@using YourApplication.YourClasses;
3) Quando fai riferimento al tuoOggetto in _Layout, lancialo. Puoi applicare il cast a causa di ciò che hai fatto in (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; }
}
Usa IContainsMyModel nel tuo layout.
Risolto. Regola delle interfacce.
Per esempio
@model IList<Model.User>
@{
Layout="~/Views/Shared/SiteLayout.cshtml";
}
Ulteriori informazioni sulla nuova direttiva @model