繁体   English   中英

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

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

我看到的是一个字符串布局属性。 但是如何将模型明确地传递给布局?

  1. 向您的控制器(或基本控制器)添加一个名为 MainLayoutViewModel(或其他任何类型)的属性,您可以使用任何类型。
  2. 在控制器(或基本控制器)的构造函数中,实例化类型并将其设置为属性。
  3. 将其设置为 ViewData 字段(或 ViewBag)
  4. 在布局页面中,将该属性转换为您的类型。

示例:控制器:

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

}

布局页面顶部示例

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

现在,您可以在布局页面中引用变量“viewModel”,并拥有对类型对象的完全访问权限。

我喜欢这种方法,因为它是控制布局的控制器,而单个页面视图模型保持与布局无关。

MVC 核心注意事项


Mvc Core 似乎在第一次调用每个操作时吹走 ViewData/ViewBag 的内容。 这意味着在构造函数中分配 ViewData 不起作用。 然而,有效的是使用IActionFilter并在OnActionExecuting做完全相同的工作。 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; } } }

如果您遇到此问题,似乎您对视图模型的建模有点错误。

我个人永远不会输入布局页面。 但是如果你想这样做,你应该有一个你的其他视图模型继承的基本视图模型,并将你的布局输入到基本视图模型,然后你页面到特定的一次。

一个常见的解决方案是创建一个包含布局文件中使用的属性的基本视图模型,然后从基本模型继承到相应页面上使用的模型。

这种方法的问题在于,您现在将自己锁定在模型只能从其他类继承的问题中,也许您的解决方案是您无论如何都无法在想要的模型上使用继承。

我的解决方案也从基本视图模型开始:

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

    public string Title { get;}
}

然后我使用的是从 LayoutModel 继承的 LayoutModel 的通用版本,如下所示:

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

    public T PageModel { get; }
}

有了这个解决方案,我就不再需要在布局模型和模型之间进行继承了。

所以现在我可以继续使用 Layout.cshtml 中的 LayoutModel,如下所示:

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

在页面上,您可以像这样使用通用的 LayoutModel:

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

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

从您的控制器中,您只需返回一个 LayoutModel 类型的模型:

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

这是非常基本的东西,您需要做的就是创建一个基本视图模型并确保全部! 我的意思是全部! 您将使用该布局的视图将接收使用该基本模型的视图!

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

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

在 _Layout.cshtml 中:

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

在 home 控制器的 Index(例如)方法中:

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

Index.cshtml:

@model Models.SomeViewModel

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

<div class="row">

我不同意将模型传递给 _layout 是错误的,可以传递一些用户信息并且可以在控制器继承链中填充数据,因此只需要一个实现。

显然,出于更高级的目的,您应该考虑使用注入创建自定义静态contaxt,并将该模型命名空间包含在_Layout.cshtml 中。

但对于基本用户来说,这可以解决问题

你为什么不添加一个新的 Partial View 和我自己的特定控制器,将所需的模型传递给局部视图,最后使用 RenderPartial 或 RenderAction 在你的 Layout.cshtml 上渲染提到的局部视图?

我使用这种方法来显示登录用户的信息,如姓名、个人资料图片等。

老问题,但只是提一下 MVC5 开发人员的解决方案,您可以使用与视图相同的Model属性。

视图和布局中的Model属性都与同一个ViewDataDictionary对象相关联,因此您不必做任何额外的工作来将模型传递到布局页面,也不必在布局中声明@model MyModelName .

但是请注意,当您在布局中使用@Model.XXX ,intelliSense 上下文菜单不会出现,因为这里的Model是一个动态对象,就像ViewBag一样。

也许这在技术上不是处理它的正确方法,但对我来说最简单和最合理的解决方案是创建一个类并在布局中实例化它。 对于其他正确的做法,这是一次例外。 如果这比在布局中完成的更多,那么您需要认真地重新考虑您在做什么,并且在进一步推进您的项目之前可能会阅读更多教程。

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

然后在视图中

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

在 .net core 中,你甚至可以跳过它并使用依赖注入。

@inject My.Namespace.IMyLayoutModel myLayoutModel

这是那些有点阴暗的区域之一。 但是考虑到我在这里看到的极其复杂的替代方案,我认为以实用性的名义做出的例外不仅仅是一个好的例外。 特别是如果您确保保持简单并确保任何繁重的逻辑(我认为确实不应该有任何逻辑,但要求不同)在它所属的另一个类/层中。 这肯定比为了一个视图而污染所有控制器或模型要好。

还有另一种存档方式。

  1. 只需为所有控制器实现 BaseController 类

  2. BaseController类中创建一个方法,该方法返回一个 Model 类,例如。

 public MenuPageModel GetTopMenu() { var m = new MenuPageModel(); // populate your model here return m; }
  1. 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>

仅从上面,但简单的实现

索引页

@model CMS.Models.IndexViewModel 

@{
    ViewBag.PageModel = Model;    
}

布局页面

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

让我们假设您的模型是一组对象(或者可能是单个对象)。 对模型中的每个对象执行以下操作。

1) 将要显示的对象放入ViewBag。 例如:

  ViewBag.YourObject = yourObject;

2) 在 _Layout.cshtml 顶部添加 using 语句,其中包含对象的类定义。 例如:

@using YourApplication.YourClasses;

3) 当您在 _Layout 中引用 yourObject 时将其投射。 由于您在(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; }
}

在您的布局中使用 IContainsMyModel。

解决了。 接口规则。

例如

@model IList<Model.User>

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

阅读有关新@model指令的更多信息

暂无
暂无

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

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