我有一个网站,其中有一个布局页面。然而,这个布局页面包含了所有页面模型必须提供的数据,例如页面标题、页面名称以及我们实际所在的位置,用于执行一些操作的HTML Helper。此外,每个页面都有其自己的视图模型属性。
我该怎么做呢?手动输入布局似乎不是一个好主意,但我应该如何传递这些信息呢?
我有一个网站,其中有一个布局页面。然而,这个布局页面包含了所有页面模型必须提供的数据,例如页面标题、页面名称以及我们实际所在的位置,用于执行一些操作的HTML Helper。此外,每个页面都有其自己的视图模型属性。
我该怎么做呢?手动输入布局似乎不是一个好主意,但我应该如何传递这些信息呢?
如果您需要将相同的属性传递给每个页面,则创建一个基础视图模型并由所有视图模型使用会很明智。然后,您的布局页面可以采用此基础模型。
如果需要处理这些数据背后的逻辑,则应将其放入基本控制器中,并由所有控制器使用。
有许多事情可以做,重要的方法是不要在多个位置重复相同的代码。
编辑:以下来自评论的更新
这里有一个简单的示例来演示该概念。
创建一个所有视图模型将继承的基础视图模型。
public abstract class ViewModelBase
{
public string Name { get; set; }
}
public class HomeViewModel : ViewModelBase
{
}
您的布局页面可以将此作为其模板。
@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>
最后在操作方法中设置数据。
public class HomeController
{
public ActionResult Index()
{
return this.View(new HomeViewModel { Name = "Bacon" });
}
}
public class HomeController : BaseController
。这样,公共代码只需要编写一次,就可以应用到所有控制器上。 - Colin Bacon我在布局中使用了Razor的RenderAction HTML助手。
@{
Html.RenderAction("Action", "Controller");
}
我需要它来处理简单的字符串。因此,我的动作返回字符串并在视图中轻松地将其写下。但是如果您需要复杂的数据,则可以返回PartialViewResult和model。
public PartialViewResult Action()
{
var model = someList;
return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
}
你只需要将你的模型放在你创建的局部视图 '_maPartialView.cshtml' 的开头处即可。@model List<WhatEverYourObjeIs>
然后您可以在该部分视图中使用模型中的数据与HTML一起使用。
OnActionExecuting
中填充模型。使用ViewBag也意味着你在控制器中失去了类型安全性,这是不好的事情。 - Colin Bacon大概来说,这个主要用例是为所有(或大部分)控制器操作获取一个基本模型以供查看。
鉴于此,我使用了几个答案的组合,主要是借鉴了 Colin Bacon 的答案。
这是控制器逻辑,因为我们正在填充一个视图模型以返回到视图。因此,放置它的正确位置是在控制器中。
我们希望所有控制器上都发生这种情况,因为我们将其用于布局页面。我将其用于在布局页中呈现的部分视图。
我们还希望获得强类型视图模型的附加好处
因此,我创建了一个 BaseViewModel 和 BaseController。所有 ViewModel 控制器都将分别继承自 BaseViewModel 和 BaseController。
代码:
BaseController
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!";
}
}
请注意使用 OnActionExecuted,该方法取自此 SO 帖子
HomeController
public class HomeController : BaseController
{
public ActionResult Index(string id)
{
HomeIndexModel model = new HomeIndexModel();
// populate HomeIndexModel ...
return View(model);
}
}
基础视图模型
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
}
页脚模型
public class FooterModel
{
public string FooterModelProperty { get; set; }
}
Layout.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>
希望这可以帮助。
@inject IService myService
@if (await myService.GetBoolValue()) {
// Good to go...
}
@inject
是最好的解决方案。 - Sergey Kalinichenko您不必修改操作或更改模型,只需使用基本控制器并将现有控制器从布局视图上下文转换。
创建一个带有所需公共数据(标题/页面/位置等)和操作初始化的基本控制器...
public abstract class _BaseController:Controller {
public Int32 MyCommonValue { get; private set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
MyCommonValue = 12345;
base.OnActionExecuting(filterContext);
}
}
确保每个控制器都使用基础控制器...
public class UserController:_BaseController {...
_Layout.cshml
页面中,将现有的基础控制器从视图上下文转换为...@{
var myController = (_BaseController)ViewContext.Controller;
}
@myController.MyCommonValue
更新
您也可以创建一个页面扩展,以允许您使用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;
}
当你需要使用控制器时,只需记住使用this.Controller()
。
@{
var myController = this.Controller(); //_BaseController
}
_BaseController
的特定控制器...@{
var myController = this.Controller<MyControllerType>();
}
@model ViewAsModelBase
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8"/>
<link href="/img/phytech_icon.ico" rel="shortcut icon" type="image/x-icon" />
<title>@ViewBag.Title</title>
@RenderSection("styles", required: false)
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
@RenderSection("scripts", required: false)
@RenderSection("head", required: false)
</head>
<body>
@Html.Action("_Header","Controller", new {model = Model})
<section id="content">
@RenderBody()
</section>
@RenderSection("footer", required: false)
</body>
</html>
并将以下代码添加到控制器中:
public ActionResult _Header(ViewAsModelBase model)
我觉得这些答案都不够灵活,无法适用于大型企业级应用。虽然我不是ViewBag的忠实拥趸,但在这种情况下,为了提高灵活性,我会做个例外。以下是我的处理方法...
你应该在所有控制器上都有一个基本控制器。在基本控制器的OnActionExecuting中添加布局数据(如果你想推迟,则使用OnActionExecuted)...
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext
filterContext)
{
ViewBag.LayoutViewModel = MyLayoutViewModel;
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
return View(homeModel);
}
}
@{
LayoutViewModel model = (LayoutViewModel)ViewBag.LayoutViewModel;
}
<h1>@model.Title</h1>
<h1>@ViewBag.LayoutViewModel.Title</h1>
这样做不会干扰您页面的控制器或视图模型的编码。
MyLayoutViewModel
,我该如何向 OnActionExecuting
方法传递一些参数呢? - ReyOnActionExecuting
方法中还需要 base.OnActionExecuting(filterContext)
呢! - ErikECustomersViewModel : LayoutNavigationViewModel
吗?为什么?为什么应该通过每个解决方案中的单个视图模型传递导航模型数据?_Layout.cshtml
文件中这样做:@{ var model = DependencyResolver.Current.GetService<MyNamespace.LayoutViewModel>(); }
new LayoutViewModel()
,我们将获得LayoutViewModel
所有的依赖项,并为我们解决它们。public class LayoutViewModel
{
private readonly DataContext dataContext;
private readonly ApplicationUserManager userManager;
public LayoutViewModel(DataContext dataContext, ApplicationUserManager userManager)
{
}
}
Scoped
布局模型对象也是一个不错的想法。 - James WilkinsModel
数据注入到_Layout
视图中。View Model
数据、Json
、Script
、CSS
、HTML
等内容。Index
视图中的Json
注入到Layout
视图中。
Index.chtml
@section commonLayoutData{
<script>
var products = @Html.Raw(Json.Encode(Model.ToList()));
</script>
}
_Layout.cshtml
@RenderSection("commonLayoutData", false)
ViewBag
中。可能只是个人偏好问题。不过我还是给你的评论点了赞。 - JP Hellemons