简体   繁体   English

ASP.NET MVC Razor 将模型传递给布局

[英]ASP.NET MVC Razor pass model to layout

What I see is a string Layout property.我看到的是一个字符串布局属性。 But how can I pass a model to layout explicitly?但是如何将模型明确地传递给布局?

  1. Add a property to your controller (or base controller) called MainLayoutViewModel (or whatever) with whatever type you would like to use.向您的控制器(或基本控制器)添加一个名为 MainLayoutViewModel(或其他任何类型)的属性,您可以使用任何类型。
  2. In the constructor of your controller (or base controller), instantiate the type and set it to the property.在控制器(或基本控制器)的构造函数中,实例化类型并将其设置为属性。
  3. Set it to the ViewData field (or ViewBag)将其设置为 ViewData 字段(或 ViewBag)
  4. In the Layout page, cast that property to your type.在布局页面中,将该属性转换为您的类型。

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

}

Example top of Layout Page布局页面顶部示例

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

Now you can reference the variable 'viewModel' in your layout page with full access to the typed object.现在,您可以在布局页面中引用变量“viewModel”,并拥有对类型对象的完全访问权限。

I like this approach because it is the controller that controls the layout, while the individual page viewmodels remain layout agnostic.我喜欢这种方法,因为它是控制布局的控制器,而单个页面视图模型保持与布局无关。

Notes for MVC Core MVC 核心注意事项


Mvc Core appears to blow away the contents of ViewData/ViewBag upon calling each action the first time. Mvc Core 似乎在第一次调用每个操作时吹走 ViewData/ViewBag 的内容。 What this means is that assigning ViewData in the constructor doesn't work. 这意味着在构造函数中分配 ViewData 不起作用。 What does work, however, is using an IActionFilter and doing the exact same work in OnActionExecuting . 然而,有效的是使用IActionFilter并在OnActionExecuting做完全相同的工作。 Put MyActionFilter on your MyController . 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; } } }

Seems like you have modeled your viewmodels a bit wrong if you have this problem.如果您遇到此问题,似乎您对视图模型的建模有点错误。

Personally I would never type a layout page.我个人永远不会输入布局页面。 But if you want to do that you should have a base viewmodel that your other viewmodels inherits from and type your layout to the base viewmodel and you pages to the specific once.但是如果你想这样做,你应该有一个你的其他视图模型继承的基本视图模型,并将你的布局输入到基本视图模型,然后你页面到特定的一次。

A common solution is to make a base view model which contains the properties used in the layout file and then inherit from the base model to the models used on respective pages.一个常见的解决方案是创建一个包含布局文件中使用的属性的基本视图模型,然后从基本模型继承到相应页面上使用的模型。

The problem with this approach is that you now have locked yourself into the problem of a model can only inherit from one other class, and maybe your solution is such that you cannot use inheritance on the model you intended anyways.这种方法的问题在于,您现在将自己锁定在模型只能从其他类继承的问题中,也许您的解决方案是您无论如何都无法在想要的模型上使用继承。

My solution also starts of with a base view model:我的解决方案也从基本视图模型开始:

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

    public string Title { get;}
}

What I then use is a generic version of the LayoutModel which inherits from the LayoutModel, like this:然后我使用的是从 LayoutModel 继承的 LayoutModel 的通用版本,如下所示:

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

    public T PageModel { get; }
}

With this solution I have disconnected the need of having inheritance between the layout model and the model.有了这个解决方案,我就不再需要在布局模型和模型之间进行继承了。

So now I can go ahead and use the LayoutModel in Layout.cshtml like this:所以现在我可以继续使用 Layout.cshtml 中的 LayoutModel,如下所示:

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

And on a page you can use the generic LayoutModel like this:在页面上,您可以像这样使用通用的 LayoutModel:

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

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

From your controller you simply return a model of type LayoutModel:从您的控制器中,您只需返回一个 LayoutModel 类型的模型:

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

this is pretty basic stuff, all you need to do is to create a base view model and make sure ALL!这是非常基本的东西,您需要做的就是创建一个基本视图模型并确保全部! and i mean ALL!我的意思是全部! of your views that will ever use that layout will receive views that use that base model!您将使用该布局的视图将接收使用该基本模型的视图!

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

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

in the _Layout.cshtml:在 _Layout.cshtml 中:

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

in the the Index (for example) method in the home controller:在 home 控制器的 Index(例如)方法中:

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

the Index.cshtml: Index.cshtml:

@model Models.SomeViewModel

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

<div class="row">

i disagree that passing a model to the _layout is an error, some user info can be passed and the data can be populate in the controllers inheritance chain so only one implementation is needed.我不同意将模型传递给 _layout 是错误的,可以传递一些用户信息并且可以在控制器继承链中填充数据,因此只需要一个实现。

obviously for more advanced purpose you should consider creating custom static contaxt using injection and include that model namespace in the _Layout.cshtml.显然,出于更高级的目的,您应该考虑使用注入创建自定义静态contaxt,并将该模型命名空间包含在_Layout.cshtml 中。

but for basic users this will do the trick但对于基本用户来说,这可以解决问题

Why dont you just add a new Partial View with i's own specific controller passing the required model to the partial view and finally Render the mentioned partial view on your Layout.cshtml using RenderPartial or RenderAction ?你为什么不添加一个新的 Partial View 和我自己的特定控制器,将所需的模型传递给局部视图,最后使用 RenderPartial 或 RenderAction 在你的 Layout.cshtml 上渲染提到的局部视图?

I use this method for showing the logged in user's info like name , profile picture and etc.我使用这种方法来显示登录用户的信息,如姓名、个人资料图片等。

old question but just to mention the solution for MVC5 developers, you can use the Model property same as in view.老问题,但只是提一下 MVC5 开发人员的解决方案,您可以使用与视图相同的Model属性。

The Model property in both view and layout is assosiated with the same ViewDataDictionary object, so you don't have to do any extra work to pass your model to the layout page, and you don't have to declare @model MyModelName in the layout.视图和布局中的Model属性都与同一个ViewDataDictionary对象相关联,因此您不必做任何额外的工作来将模型传递到布局页面,也不必在布局中声明@model MyModelName .

But notice that when you use @Model.XXX in the layout the intelliSense context menu will not appear because the Model here is a dynamic object just like ViewBag .但是请注意,当您在布局中使用@Model.XXX ,intelliSense 上下文菜单不会出现,因为这里的Model是一个动态对象,就像ViewBag一样。

Maybe it isnt technically the proper way to handle it, but the simplest and most reasonable solution for me is to just make a class and instantiate it in the layout.也许这在技术上不是处理它的正确方法,但对我来说最简单和最合理的解决方案是创建一个类并在布局中实例化它。 It is a one time exception to the otherwise correct way of doing it.对于其他正确的做法,这是一次例外。 If this is done more than in the layout then you need to seriously rethink what your doing and maybe read a few more tutorials before progressing further in your project.如果这比在布局中完成的更多,那么您需要认真地重新考虑您在做什么,并且在进一步推进您的项目之前可能会阅读更多教程。

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

then in the view然后在视图中

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

in .net core you can even skip that and use dependency injection.在 .net core 中,你甚至可以跳过它并使用依赖注入。

@inject My.Namespace.IMyLayoutModel myLayoutModel

It is one of those areas that is kind of shady.这是那些有点阴暗的区域之一。 But given the extremely over complicated alternatives I am seeing here, I think it is more than an ok exception to make in the name of practicality.但是考虑到我在这里看到的极其复杂的替代方案,我认为以实用性的名义做出的例外不仅仅是一个好的例外。 Especially if you make sure to keep it simple and make sure any heavy logic (I would argue that there really shouldnt be any, but requirements differ) is in another class/layer where it belongs.特别是如果您确保保持简单并确保任何繁重的逻辑(我认为确实不应该有任何逻辑,但要求不同)在它所属的另一个类/层中。 It is certainly better than polluting ALL of your controllers or models for the sake of basically just one view..这肯定比为了一个视图而污染所有控制器或模型要好。

There is another way to archive it.还有另一种存档方式。

  1. Just implement BaseController class for all controllers .只需为所有控制器实现 BaseController 类

  2. In the BaseController class create a method that returns a Model class like for instance.BaseController类中创建一个方法,该方法返回一个 Model 类,例如。

 public MenuPageModel GetTopMenu() { var m = new MenuPageModel(); // populate your model here return m; }
  1. And in the Layout page you can call that method GetTopMenu()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>

From above only, but simple implementation仅从上面,但简单的实现

Index Page索引页

@model CMS.Models.IndexViewModel 

@{
    ViewBag.PageModel = Model;    
}

Layout Page布局页面

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

Let's assume your model is a collection of objects (or maybe a single object).让我们假设您的模型是一组对象(或者可能是单个对象)。 For each object in the model do the following.对模型中的每个对象执行以下操作。

1) Put the object you want to display in the ViewBag. 1) 将要显示的对象放入ViewBag。 For example:例如:

  ViewBag.YourObject = yourObject;

2) Add a using statement at the top of _Layout.cshtml that contains the class definition for your objects. 2) 在 _Layout.cshtml 顶部添加 using 语句,其中包含对象的类定义。 For example:例如:

@using YourApplication.YourClasses; @using YourApplication.YourClasses;

3) When you reference yourObject in _Layout cast it. 3) 当您在 _Layout 中引用 yourObject 时将其投射。 You can apply the cast because of what you did in (2).由于您在(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 in your layout.在您的布局中使用 IContainsMyModel。

Solved.解决了。 Interfaces rule.接口规则。

For example例如

@model IList<Model.User>

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

Read more about the new @model directive阅读有关新@model指令的更多信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM