web-dev-qa-db-fra.com

ASP.NET MVC Razor passe le modèle à la présentation

Ce que je vois est une propriété Layout de chaîne. Mais comment puis-je passer un modèle à la mise en page explicitement?

84
SiberianGuy

On dirait que vous avez un peu faussé la modélisation de vos modèles de vue si vous avez ce problème. 

Personnellement, je ne taperais jamais une page de mise en page. Mais si vous voulez faire cela, vous devez avoir un modèle de vue de base dont héritent vos autres modèles de vue, taper votre mise en page dans le modèle de base et vos pages dans une fois.

65
Mattias Jakobsson
  1. Ajoutez une propriété à votre contrôleur (ou contrôleur de base) appelée MainLayoutViewModel (ou autre) avec le type que vous souhaitez utiliser.
  2. Dans le constructeur de votre contrôleur (ou contrôleur de base), instanciez le type et définissez-le sur la propriété.
  3. Définissez-le sur le champ ViewData (ou ViewBag)
  4. Dans la page Disposition, transformez cette propriété en votre type.

Exemple: Contrôleur:

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

}

Exemple en haut de la page de mise en page

@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}

Vous pouvez maintenant référencer la variable 'viewModel' dans votre page de disposition avec un accès complet à l'objet saisi.

J'aime cette approche car c'est le contrôleur qui contrôle la présentation, tandis que les modèles de vue de page individuels restent indépendants de la présentation.

Notes pour MVC Core


Mvc Core semble détruire le contenu de ViewData/ViewBag lors du premier appel de chaque action. Cela signifie que l'assignation de ViewData dans le constructeur ne fonctionne pas. Ce qui fonctionne, cependant, utilise une IActionFilter et effectue exactement le même travail dans OnActionExecuting. Mettez MyActionFilter sur votre 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;
            }
        }
    }
70
BlackjacketMack

c’est plutôt simple, il suffit de créer un modèle de vue de base et d’assurer TOUT! et je veux dire TOUS! de vos vues qui utiliseront jamais cette disposition recevront des vues qui utilisent ce modèle de base!

public class SomeViewModel : ViewModelBase
{
    public bool ImNotEmpty = true;
}

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

dans le _Layout.cshtml:

@model Models.ViewModelBase
<!DOCTYPE html>
  <html>
  and so on...

dans la méthode Index (par exemple) du contrôleur d'origine:

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

le Index.cshtml:

@model Models.SomeViewModel

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

<div class="row">

je ne suis pas d'accord sur le fait que passer un modèle à _layout est une erreur, certaines informations utilisateur peuvent être transmises et les données peuvent être renseignées dans la chaîne d'héritage des contrôleurs, de sorte qu'une seule implémentation est nécessaire.

évidemment, pour des raisons plus avancées, vous devriez envisager de créer un contact statique personnalisé en utilisant l’injection et inclure cet espace de nom de modèle dans _Layout.cshtml. 

mais pour les utilisateurs de base cela fera l'affaire

23
Yakir Manor

Pourquoi ne pas simplement ajouter une nouvelle vue partielle avec le contrôleur spécifique de i en transmettant le modèle requis à la vue partielle et enfin restituer la vue partielle mentionnée sur votre Layout.cshtml en utilisant RenderPartial ou RenderAction? 

J'utilise cette méthode pour afficher les informations de l'utilisateur connecté, telles que le nom, la photo de profil, etc. 

11
Arya Sh

Une solution courante consiste à créer un modèle de vue de base contenant les propriétés utilisées dans le fichier de présentation, puis à hériter du modèle de base aux modèles utilisés sur les pages respectives. 

Le problème avec cette approche est que vous vous êtes maintenant enfermés dans le problème d’un modèle qui ne peut hériter que d’une autre classe, et peut-être que votre solution est telle que vous ne pouvez pas utiliser l’héritage sur le modèle que vous souhaitez de toute façon.

Ma solution commence également par un modèle de vue de base:

public class LayoutModel
{
    public LayoutModel(string title)
    {
        Title = title;
    }

    public string Title { get;}
}

Ce que j’utilise ensuite est une version générique du LayoutModel qui hérite du LayoutModel, comme ceci:

public class LayoutModel<T> : LayoutModel
{
    public LayoutModel(T pageModel, string title) : base(title)
    {
        PageModel = pageModel;
    }

    public T PageModel { get; }
}

Avec cette solution, j'ai dissocié la nécessité d'avoir un héritage entre le modèle de mise en page et le modèle.

Alors maintenant, je peux utiliser le LayoutModel dans Layout.cshtml comme ceci:

@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>

Et sur une page, vous pouvez utiliser le LayoutModel générique comme ceci:

@model LayoutModel<Customer>
@{
    var customer = Model.PageModel;
}

<p>Customer name: @customer.Name</p>

Depuis votre contrôleur, vous retournez simplement un modèle de type LayoutModel:

public ActionResult Page()
{
    return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}
8
Oskar Sjöberg

Ce n’est peut-être pas techniquement la meilleure façon de le gérer, mais la solution la plus simple et la plus raisonnable pour moi est de créer un cours et de l’instancier dans la mise en page. Il s’agit d’une exception unique à la manière par ailleurs correcte de procéder. Si cela se fait plus que dans la mise en page, vous devez repenser sérieusement ce que vous faites et peut-être lire quelques tutoriels supplémentaires avant de poursuivre votre projet.

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

alors dans la vue

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

dans le noyau .net, vous pouvez même ignorer cela et utiliser l’injection de dépendance.

@inject My.Namespace.IMyLayoutModel myLayoutModel

C'est un de ces domaines qui est un peu louche. Mais étant donné les alternatives extrêmement compliquées que je vois ici, je pense que c’est plus qu’une exception acceptable à faire au nom de la praticité. Surtout si vous veillez à rester simple et à vous assurer que toute logique lourde (je dirais qu'il ne devrait vraiment pas en exister, mais que les exigences diffèrent) est dans une autre classe/couche à laquelle elle appartient. C’est certainement mieux que de polluer TOUS vos contrôleurs ou modèles pour un seul point de vue.

1
computrius

vieille question, mais pour ne mentionner que la solution pour les développeurs MVC5, vous pouvez utiliser la propriété Model comme dans view. 

La propriété Model à la fois dans l'affichage et dans la présentation est associée au même objet ViewDataDictionary. Vous n'avez donc pas à effectuer de travail supplémentaire pour transmettre votre modèle à la page de présentation, ni à déclarer @model MyModelName dans la présentation.

Mais notez que lorsque vous utilisez @Model.XXX dans la présentation, le menu contextuel d’intelliSense ne s’affiche pas car la Model ici est un objet dynamique similaire à ViewBag.

1
Laz Ziya

Supposons que votre modèle est une collection d'objets (ou peut-être un seul objet). Procédez comme suit pour chaque objet du modèle.

1) Placez l'objet que vous souhaitez afficher dans le ViewBag. Par exemple:

  ViewBag.YourObject = yourObject;

2) Ajoutez une instruction using en haut de _Layout.cshtml qui contient la définition de classe pour vos objets. Par exemple:

@ using YourApplication.YourClasses;

3) Lorsque vous référencez yourObject dans _Layout, lancez-le. Vous pouvez appliquer le casting à cause de ce que vous avez fait dans (2).

0
HappyPawn8