レイアウトページのあるウェブサイトを持っています。ただし、このレイアウトページには、すべてのページモデルがそのようなページタイトル、ページ名、およびアクションを実行するHTMLヘルパーの実際の場所を提供する必要があるデータがあります。また、各ページには独自のビューモデルプロパティがあります。
これどうやってするの?レイアウトを入力するのは悪い考えのようですが、どうすればこれらの情報を渡すことができますか?
ViewBag
。おそらく好みの問題です。しかし、コメントに賛成しました
レイアウトページのあるウェブサイトを持っています。ただし、このレイアウトページには、すべてのページモデルがそのようなページタイトル、ページ名、およびアクションを実行するHTMLヘルパーの実際の場所を提供する必要があるデータがあります。また、各ページには独自のビューモデルプロパティがあります。
これどうやってするの?レイアウトを入力するのは悪い考えのようですが、どうすればこれらの情報を渡すことができますか?
ViewBag
。おそらく好みの問題です。しかし、コメントに賛成しました
回答:
各ページに同じプロパティを渡す必要がある場合は、すべてのビューモデルで使用されるベースビューモデルを作成するのが賢明です。レイアウトページは、この基本モデルを取ることができます。
このデータの背後に必要なロジックがある場合、これは、すべてのコントローラーで使用される基本コントローラーに配置する必要があります。
できることはたくさんありますが、重要なアプローチは、同じコードを複数の場所で繰り返さないことです。
編集:以下のコメントから更新
これは、概念を示すための簡単な例です。
すべてのビューモデルが継承するベースビューモデルを作成します。
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
。このようにして、共通コードは一度だけ作成する必要があり、すべてのコントローラーに適用できます。
レイアウトでかみそりにRenderAction htmlヘルパーを使用しました。
@{
Html.RenderAction("Action", "Controller");
}
単純な文字列に必要でした。だから私の行動は文字列を返し、それをビューに簡単に書き留めます。ただし、複雑なデータが必要な場合は、PartialViewResultとモデルを返すことができます。
public PartialViewResult Action()
{
var model = someList;
return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
}
作成した部分ビュー '_maPartialView.cshtml'で始まるモデルを配置するだけです
@model List<WhatEverYourObjeIs>
次に、HTMLを使用して、その部分ビューでモデルのデータを使用できます。
別のオプションは、レイアウトに必要なすべてのプロパティを備えた別のLayoutModelクラスを作成し、このクラスのインスタンスをViewBagに詰め込むことです。Controller.OnActionExecutingメソッドを使用して、データを設定します。次に、レイアウトの開始時に、このオブジェクトをViewBagからプルバックして、この強く型付けされたオブジェクトに引き続きアクセスできます。
OnActionExecuting
。ViewBagを使用することは、コントローラーのタイプセーフを失うことも意味し、決して良いことではありません。
おそらく、これの主な使用例は、すべて(または大部分)のコントローラーアクションのビューに基本モデルを取得することです。
それを踏まえて、私はこれらの回答のいくつかを組み合わせて使用しました。主な便乗は、コリンベーコンの回答です。
ビューモデルにデータを入力してビューに戻るので、これは依然としてコントローラーロジックです。したがって、これを配置する適切な場所は、コントローラ内です。
これをレイアウトページに使用するため、すべてのコントローラーでこれが発生するようにします。レイアウトページでレンダリングされる部分ビューに使用しています。
強く型付けされたViewModelの追加の利点も必要です
したがって、私はBaseViewModelとBaseControllerを作成しました。すべてのViewModelsコントローラは、それぞれ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!";
}
}
このSO投稿から取得したOnActionExecutedの使用に注意してください
HomeController
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; }
}
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>
うまくいけば、これが役立ちます。
アクションをいじったりモデルを変更したりする必要はありません。基本コントローラーを使用して、レイアウトビューのコンテキストから既存のコントローラーをキャストするだけです。
必要な共通データ(タイトル/ページ/場所など)とアクションの初期化を使用して基本コントローラーを作成します...
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);
}
}
次に、_Layout.cshtmlでViewModelをViewBagから取得します...
@{
LayoutViewModel model = (LayoutViewModel)ViewBag.LayoutViewModel;
}
<h1>@model.Title</h1>
または...
<h1>@ViewBag.LayoutViewModel.Title</h1>
これを行うことは、ページのコントローラーまたはビューモデルのコーディングに干渉しません。
MyLayoutViewModel
動的に作成した場合はどうすれば、いくつかのパラメーターをOnActionExecuting
メソッドに渡すことができますか?
base.OnActionExecuting(filterContext)
あなたのOnActionExecuting
方法で必要です!!!
レイアウトビューモデルを表すベースビューを作成するのはひどいアプローチです。レイアウトで定義されたナビゲーションを表すモデルが必要だと想像してください。しますかCustomersViewModel : 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
ASP..Net Coreのレイアウトモデルオブジェクトにも適していると思います。
他の回答では、モデルをレイアウトページに渡す方法に関するほとんどすべてをカバーしています。しかし、レイアウトでモデルや部分ビューを使用せずに、レイアウトページに動的に変数を渡す方法を見つけました。あなたがこのモデルを持っているとしましょう-
public class SubLocationsViewModel
{
public string city { get; set; }
public string state { get; set; }
}
そして、都市と州を動的に取得したいとします。例えば
index.cshtmlでは、これら2つの変数をViewBagに配置できます
@model MyProject.Models.ViewModel.SubLocationsViewModel
@{
ViewBag.City = Model.city;
ViewBag.State = Model.state;
}
そして、あなたのlayout.cshtmlであなたはそれらのviewbag変数にアクセスできます
<div class="text-wrap">
<div class="heading">@ViewBag.City @ViewBag.State</div>
</div>
これを処理する別の方法があります。おそらく、アーキテクチャの観点からは最もクリーンな方法ではありませんが、他の回答に伴う多くの苦痛を回避できます。Razorレイアウトにサービスを注入し、必要なデータを取得するメソッドを呼び出すだけです。
@inject IService myService
その後、後でレイアウトビューで:
@if (await myService.GetBoolValue()) {
// Good to go...
}
繰り返しますが、アーキテクチャの点ではクリーンではありません(もちろん、サービスをビューに直接注入するべきではありません)が、それで仕事が完了します。
@inject
私の意見では、使用することが最善の解決策です。
RenderSectionを利用することもできます。これModel
は、_Layout
ビューにデータを注入するのに役立ちます。
あなたは、注入することができView Model
、データ、Json
、Script
、CSS
、HTML
など
この例ではJson
、Index
ビューからLayout
ビューに注入しています。
Index.chtml
@section commonLayoutData{
<script>
var products = @Html.Raw(Json.Encode(Model.ToList()));
</script>
}
_Layout.cshtml
@RenderSection("commonLayoutData", false)
これにより、別個のBaseを作成する必要がなくなりView Model
ます。
希望は誰かを助けます。
私がしたことは非常に簡単で、うまくいきました
任意のコントローラーで静的プロパティを宣言するか、次のようにしたい場合は静的値でデータクラスを作成できます。
public static username = "Admin";
public static UserType = "Administrator";
これらの値は、操作に基づいてコントローラーによって更新できます。後で_Layoutで使用できます
_layout.cshtml
@project_name.Controllers.HomeController.username
@project_name.Controllers.HomeController.UserType
ViewDataで拡張メソッドが提案されていないのはなぜですか?
オプション1
私にとっては、この問題に対する最も煩わしくなく、最も簡単な解決策のようです。ハードコードされた文字列はありません。課された制限はありません。魔法のコーディングはありません。複雑なコードはありません。
public static class ViewDataExtensions
{
private const string TitleData = "Title";
public static void SetTitle<T>(this ViewDataDictionary<T> viewData, string value) => viewData[TitleData] = value;
public static string GetTitle<T>(this ViewDataDictionary<T> viewData) => (string)viewData[TitleData] ?? "";
}
ページにデータを設定する
ViewData.SetTitle("abc");
オプション#2
フィールド宣言を簡単にする別のオプション。
public static class ViewDataExtensions
{
public static ViewDataField<string, V> Title<V>(this ViewDataDictionary<V> viewData) => new ViewDataField<string, V>(viewData, "Title", "");
}
public class ViewDataField<T,V>
{
private readonly ViewDataDictionary<V> _viewData;
private readonly string _field;
private readonly T _defaultValue;
public ViewDataField(ViewDataDictionary<V> viewData, string field, T defaultValue)
{
_viewData = viewData;
_field = field;
_defaultValue = defaultValue;
}
public T Value {
get => (T)(_viewData[_field] ?? _defaultValue);
set => _viewData[_field] = value;
}
}
ページにデータを設定します。宣言は最初のオプションより簡単ですが、使用構文は少し長くなります。
ViewData.Title().Value = "abc";
オプション#3
次に、それを組み合わせて、すべてのレイアウト関連フィールドとデフォルト値を含む単一のオブジェクトを返すことができます。
public static class ViewDataExtensions
{
private const string LayoutField = "Layout";
public static LayoutData Layout<T>(this ViewDataDictionary<T> viewData) =>
(LayoutData)(viewData[LayoutField] ?? (viewData[LayoutField] = new LayoutData()));
}
public class LayoutData
{
public string Title { get; set; } = "";
}
ページにデータを設定する
var layout = ViewData.Layout();
layout.Title = "abc";
この3番目のオプションにはいくつかの利点があり、ほとんどの場合、私は最良のオプションだと思います。
フィールドとデフォルト値の最も簡単な宣言。
複数のフィールドを設定する場合の最も簡単な使用法の構文。
ViewDataでさまざまな種類のデータを設定できます(レイアウト、ヘッダー、ナビゲーションなど)。
LayoutDataクラス内で追加のコードとロジックを許可します。
PS _ViewImports.cshtmlにViewDataExtensionsの名前空間を追加することを忘れないでください
App_Codeフォルダーにかみそりファイルを作成し、ビューページからアクセスできます。
プロジェクト> Repository / IdentityRepository.cs
namespace Infrastructure.Repository
{
public class IdentityRepository : IIdentityRepository
{
private readonly ISystemSettings _systemSettings;
private readonly ISessionDataManager _sessionDataManager;
public IdentityRepository(
ISystemSettings systemSettings
)
{
_systemSettings = systemSettings;
}
public string GetCurrentUserName()
{
return HttpContext.Current.User.Identity.Name;
}
}
}
Project> App_Code / IdentityRepositoryViewFunctions.cshtml:
@using System.Web.Mvc
@using Infrastructure.Repository
@functions
{
public static IIdentityRepository IdentityRepositoryInstance
{
get { return DependencyResolver.Current.GetService<IIdentityRepository>(); }
}
public static string GetCurrentUserName
{
get
{
var identityRepo = IdentityRepositoryInstance;
if (identityRepo != null)
{
return identityRepo.GetCurrentUserName();
}
return null;
}
}
}
Project> Views / Shared / _Layout.cshtml(またはその他の.cshtmlファイル)
<div>
@IdentityRepositoryViewFunctions.GetCurrentUserName
</div>
これを通過する代わりに、常に高速な別のアプローチを使用できます
共有ディレクトリに新しい部分ビューを作成し、次のようにレイアウトで部分ビューを呼び出します
@Html.Partial("MyPartialView")
部分ビューでは、dbを呼び出して、やりたいことを実行できます
@{
IEnumerable<HOXAT.Models.CourseCategory> categories = new HOXAT.Models.HOXATEntities().CourseCategories;
}
<div>
//do what ever here
</div>
Entity Frameworkデータベースを追加したと仮定します
ここでこれを誰も言ったことがないのは信じられないことです。ベースコントローラーを介してビューモデルを渡すのはごちゃごちゃです。ユーザークレームを使用して、レイアウトページに情報を渡します(ナビゲーションバーにユーザーデータを表示するためなど)。もう1つ利点があります。データはCookieを介して保存されるため、パーシャルを介して各リクエストのデータを取得する必要はありません。グーグルで「ASPネットIDクレーム」を実行してください。
次のように使用できます:
@{
ApplicationDbContext db = new ApplicationDbContext();
IEnumerable<YourModel> bd_recent = db.YourModel.Where(m => m.Pin == true).OrderByDescending(m=>m.ID).Select(m => m);
}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-body">
<div class="baner1">
<h3 class="bb-hred">Recent Posts</h3>
@foreach(var item in bd_recent)
{
<a href="/BaiDangs/BaiDangChiTiet/@item.ID">@item.Name</a>
}
</div>
</div>
</div>
</div>