135 votes

Transmettre des données à la mise en page qui sont communes à toutes les pages

J'ai un site web qui a une page de mise en page. Cependant, cette page de mise en page contient des données que toutes les pages doivent fournir, comme le titre de la page, le nom de la page et l'emplacement où nous nous trouvons actuellement pour une aide HTML que j'ai créée et qui effectue une action. De plus, chaque page a ses propres propriétés de modèle de vue.

Comment puis-je faire cela ? Il semble que ce ne soit pas une bonne idée de taper une mise en page mais comment puis-je passer ces informations ?

12 votes

Pour tous ceux qui lisent les réponses ici, veuillez voir stackoverflow.com/a/21130867/706346 où vous verrez une solution beaucoup plus simple et plus propre que tout ce qui a été posté ici.

5 votes

@AvrohomYisroel bonne suggestion. Cependant, je préfère l'approche de @Colin Bacon parce qu'elle est fortement typée et qu'elle n'est pas dans l'approche de la ViewBag . Peut-être une question de préférences. J'ai quand même upvoted votre commentaire

0 votes

Pour mvc 5 voir cette réponse : stackoverflow.com/a/46783375/5519026

148voto

Colin Bacon Points 4602

Si vous devez transmettre les mêmes propriétés à chaque page, il serait judicieux de créer un modèle de vue de base utilisé par tous vos modèles de vue. Votre page de mise en page peut alors prendre ce modèle de base.

Si une logique est nécessaire derrière ces données, elle doit être placée dans un contrôleur de base qui est utilisé par tous vos contrôleurs.

Il y a beaucoup de choses que vous pourriez faire, l'approche importante étant de ne pas répéter le même code à plusieurs endroits.

Edit : Mise à jour des commentaires ci-dessous

Voici un exemple simple pour démontrer le concept.

Créez un modèle de vue de base dont tous les modèles de vue hériteront.

public abstract class ViewModelBase
{
    public string Name { get; set; }
}

public class HomeViewModel : ViewModelBase
{
}

Votre page de mise en page peut le prendre comme modèle.

@model ViewModelBase
<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Test</title>
    </head>
    <body>
        <header>
            Hello @Model.Name
        </header>
        <div>
            @this.RenderBody()
        </div>
    </body>
</html>

Enfin, définissez les données dans la méthode d'action.

public class HomeController
{
    public ActionResult Index()
    {
        return this.View(new HomeViewModel { Name = "Bacon" });
    }
}

12 votes

Mais les données sont utilisées dans la mise en page. Comment puis-je transmettre les données à la mise en page ?

0 votes

Oui, j'ai pensé à ça. Cependant, je suis presque sûr que j'ai essayé et que ça n'a pas marché. Je vais réessayer pour voir ce qui n'a pas marché et revenir vers vous.

2 votes

Parfait ! J'ai vu mon erreur. J'ai oublié de passer le modèle à la vue quelle erreur boiteuse. Merci !

77voto

Burk Points 36

J'ai utilisé l'aide html RenderAction pour le rasoir dans la mise en page.

@{
   Html.RenderAction("Action", "Controller");
 }

J'en avais besoin pour une simple chaîne de caractères. Donc mon action retourne une chaîne et l'écrit facilement dans la vue. Mais si vous avez besoin de données complexes, vous pouvez retourner PartialViewResult et le modèle.

 public PartialViewResult Action()
    {
        var model = someList;
        return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
    }

Il vous suffit de placer votre modèle au début de la vue partielle '_maPartialView.cshtml' que vous avez créée.

@model List<WhatEverYourObjeIs>

Vous pouvez alors utiliser les données du modèle dans cette vue partielle avec html.

36voto

DenNukem Points 3455

Une autre option est de créer une classe LayoutModel distincte avec toutes les propriétés dont vous aurez besoin dans la mise en page, puis de placer une instance de cette classe dans ViewBag. J'utilise la méthode Controller.OnActionExecuting pour la remplir. Ensuite, au début de la mise en page, vous pouvez retirer cet objet de ViewBag et continuer à accéder à cet objet fortement typé.

1 votes

Cela semble être la solution la moins douloureuse, y a-t-il des inconvénients ? +1

2 votes

C'est certainement la meilleure solution et je ne vois pas d'inconvénients.

1 votes

Honnêtement, je pense que c'est une bien meilleure solution, la version acceptée devient folle quand on travaille avec des colections.

31voto

drizzie Points 2006

On peut supposer que le principal cas d'utilisation est l'obtention d'un modèle de base pour la vue pour toutes (ou la majorité) des actions du contrôleur.

J'ai donc utilisé une combinaison de plusieurs de ces réponses, en m'appuyant principalement sur la réponse de Colin Bacon.

Il est vrai qu'il s'agit toujours d'une logique de contrôleur, car nous remplissons un modèle de vue pour retourner à une vue. Ainsi, l'endroit correct pour placer ceci est dans le contrôleur.

Nous voulons que cela se produise sur tous les contrôleurs car nous l'utilisons pour la page de présentation. Je l'utilise pour les vues partielles qui sont rendues dans la page de mise en page.

Nous voulons également bénéficier de l'avantage supplémentaire d'un ViewModel fortement typé.

Ainsi, j'ai créé un BaseViewModel et un BaseController. Tous les ViewModel Controllers hériteront respectivement de BaseViewModel et de BaseController.

Le code :

Contrôleur de base

public class BaseController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var model = filterContext.Controller.ViewData.Model as BaseViewModel;

        model.AwesomeModelProperty = "Awesome Property Value";
        model.FooterModel = this.getFooterModel();
    }

    protected FooterModel getFooterModel()
    {
        FooterModel model = new FooterModel();
        model.FooterModelProperty = "OMG Becky!!! Another Awesome Property!";
    }
}

Notez l'utilisation de OnActionExecuted tel que pris dans ce poste SO

AccueilContrôleur

public class HomeController : BaseController
{
    public ActionResult Index(string id)
    {
        HomeIndexModel model = new HomeIndexModel();

        // populate HomeIndexModel ...

        return View(model);
    }
}

BaseViewModel

public class BaseViewModel
{
    public string AwesomeModelProperty { get; set; }
    public FooterModel FooterModel { get; set; }
}

HomeViewModel

public class HomeIndexModel : BaseViewModel
{

    public string FirstName { get; set; }

    // other awesome properties
}

FooterModel

public class FooterModel
{
    public string FooterModelProperty { get; set; }
}

Mise en page.cshtml

@model WebSite.Models.BaseViewModel
<!DOCTYPE html>
<html>
<head>
    < ... meta tags and styles and whatnot ... >
</head>
<body>
    <header>
        @{ Html.RenderPartial("_Nav", Model.FooterModel.FooterModelProperty);}
    </header>

    <main>
        <div class="container">
            @RenderBody()
        </div>

        @{ Html.RenderPartial("_AnotherPartial", Model); }
        @{ Html.RenderPartial("_Contact"); }
    </main>

    <footer>
        @{ Html.RenderPartial("_Footer", Model.FooterModel); }
    </footer>

    < ... render scripts ... >

    @RenderSection("scripts", required: false)
</body>
</html>

_Nav.cshtml

@model string
<nav>
    <ul>
        <li>
            <a href="@Model" target="_blank">Mind Blown!</a>
        </li>
    </ul>
</nav>

J'espère que cela vous aidera.

9voto

Carter Points 3877

Il n'est pas nécessaire d'intervenir sur les actions ou de modifier le modèle, il suffit d'utiliser un contrôleur de base et d'intégrer le contrôleur existant dans le contexte de la présentation.

Créer un contrôleur de base avec les données communes souhaitées (titre/page/emplacement etc) et l'initialisation de l'action...

public abstract class _BaseController:Controller {
    public Int32 MyCommonValue { get; private set; }

    protected override void OnActionExecuting(ActionExecutingContext filterContext) {

        MyCommonValue = 12345;

        base.OnActionExecuting(filterContext);
    }
}

Assurez-vous que chaque contrôleur utilise le contrôleur de base...

public class UserController:_BaseController {...

Coulez le contrôleur de base existant du contexte de vue dans votre _Layout.cshml page...

@{
    var myController = (_BaseController)ViewContext.Controller;
}

Vous pouvez maintenant faire référence aux valeurs de votre contrôleur de base à partir de votre page de mise en page.

@myController.MyCommonValue

UPDATE

Vous pourriez également créer une extension de page qui vous permettrait d'utiliser this .

//Allows typed "this.Controller()." in cshtml files
public static class MyPageExtensions {
    public static _BaseController Controller(this WebViewPage page) => Controller<_BaseController>(page);
    public static T Controller<T>(this WebViewPage page) where T : _BaseController => (T)page.ViewContext.Controller;
}

Ensuite, vous devez seulement vous rappeler d'utiliser this.Controller() quand vous voulez le contrôleur.

@{
    var myController = this.Controller(); //_BaseController
}

ou un contrôleur spécifique qui hérite de _BaseController ...

@{
    var myController = this.Controller<MyControllerType>();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X