developer tip

ASP.NET MVC Razor 모델을 레이아웃으로 전달

copycodes 2020. 9. 9. 08:09
반응형

ASP.NET MVC Razor 모델을 레이아웃으로 전달


내가 보는 것은 문자열 레이아웃 속성입니다. 하지만 어떻게 모델을 레이아웃에 명시 적으로 전달할 수 있습니까?


이 문제가 있으면 뷰 모델을 약간 잘못 모델링 한 것 같습니다.

개인적으로 레이아웃 페이지를 입력하지 않습니다. 그러나 그렇게하려면 다른 뷰 모델이 상속하는 기본 뷰 모델이 있어야하며 기본 뷰 모델에 레이아웃을 입력하고 특정 페이지로 한 번 페이지를 지정해야합니다.


  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 Core에 대한 참고 사항


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 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...

in the the Index (for example) method in the home controller:

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

the 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.

obviously for more advanced purpose you should consider creating custom static contaxt using injection and include that model namespace in the _Layout.cshtml.

but for basic users this will do the trick


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:

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:

@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:

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

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

From your controller you simply return a model of type LayoutModel:

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

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 ?

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.

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.

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.


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.

@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..


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. For example:

  ViewBag.YourObject = yourObject;

2) Add a using statement at the top of _Layout.cshtml that contains the class definition for your objects. For example:

@using YourApplication.YourClasses;

3) When you reference yourObject in _Layout cast it. You can apply the cast because of what you did in (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.

Solved. Interfaces rule.


For example

@model IList<Model.User>

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

Read more about the new @model directive

참고URL : https://stackoverflow.com/questions/4154407/asp-net-mvc-razor-pass-model-to-layout

반응형