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


97

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


모델이 다르지만 레이아웃이 같은 페이지가 여러 개 있습니다.
SiberianGuy

2
이 stackoverflow 질문은 당신이 묻는 질문에 대답하는 것 같습니다 : stackoverflow.com/questions/13225315/…
Paul

이 문제가 없습니다. Model에서 사용할 수 있습니다 _Layout. MVC5를 사용하고 있습니다.
toddmo

답변:


66

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

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


11
"개인적으로는 레이아웃 페이지를 입력하지 않습니다." 왜? 내 말은, 모든 페이지에 나타나는 측면 동적 콘텐츠를 어떻게 처리합니까? 보기에서 컨트롤러를 건너 뛰나요? / 아마도 레이아웃에서 RenderAction을 사용하려고하십니까? (나는 단지 지금보고 있습니다)
eglasius 2011

52
@eglasius, 내가 사용하는 솔루션은 우리가 말하는 내용의 종류에 따라 다릅니다. 그러나 일반적인 해결책은 RenderAction을 사용하여 레이아웃 페이지에서 자체 데이터가 필요한 부분을 렌더링하는 것입니다. 레이아웃 페이지를 입력하는 것을 좋아하지 않는 이유는 모든 특정 뷰 모델에서 항상 "기본"뷰 모델을 상속해야하기 때문입니다. 내 경험상 이것은 일반적으로 좋은 생각이 아니며 디자인을 변경하는 데 늦거나 오래 걸릴 때 문제가 발생하는 경우가 많습니다.
Mattias Jakobsson 2011

2
상속이 아닌 집계로 기본 모델을 포함하려면 어떻게해야합니까? 디자인 관점에서 완벽하게 합법적 인 방법입니다. 그러면 레이아웃을 어떻게 처리합니까?
Fyodor Soikin 2011-08-22

4
두 가지 솔루션이 있습니다. 레이아웃에 대한 일반 모델이므로 레이아웃에서만 MyViewModel과 함께 RenderPartial을 사용하여 뷰 모델에 MyLayoutModel <MyViewModel>을 사용할 수 있습니다. 또는 정적 캐시 부분에 대해 RenderAction을 사용하고 동적 부분에 대한 ajax 호출을 사용하여 페이지의 부분을 부분적으로 렌더링합니다. 그러나 검색 엔진에 더 친숙하고 ajax 업데이트와 쉽게 결합되므로 첫 번째 솔루션을 선호합니다.
Softlion 2012 년

4
정확히 이것이 수행 된 레거시 코드에서 작업합니다. 악몽입니다. 레이아웃을 입력하지 마십시오 ...

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

1
알겠습니다 ...하지만 역학 / 캐스트는 면도기 페이지의 핵심입니다. 당신이 할 수있는 한 가지는 당신을 위해 캐스팅을하는 MainLayoutViewModel에 정적 메소드를 추가하는 것입니다.
BlackjacketMack

@BlackjacketMack 좋은 접근 방식을 사용하고 위의 방법을 사용하여 몇 가지 수정 bcoz를 작성하여 diff 요구 사항이 있었고 이것은 정말 감사합니다. 예인 경우 TempData를 사용하여 동일한 결과를 얻을 수 있습니까? 그런 다음 plz가 왜 사용할 수 없는지 알려주십시오. 다시 한 번 감사드립니다.
Zaker

2
@User-TempData는 Session을 사용하고 항상 나에게 약간 어색함을 느낍니다. 내 이해는 '한 번 읽기'이므로 읽는 즉시 세션에서 제거됩니다 (또는 요청이 끝나 자마자). Sql Server (또는 Dynamo Db)에 세션을 저장할 수 있으므로 MasterLayoutViewModel을 직렬화해야한다는 사실을 고려하십시오. 따라서 기본적으로 ViewData로 설정하면 계산서에 맞는 약간의 유연한 사전에 메모리에 저장됩니다.
BlackjacketMack

충분히 간단하지만 귀하의 솔루션을 사용했지만 MVC를 처음 사용하므로 이것이 좋은 방법으로 간주되는지 궁금합니다. 아니면 적어도 나쁘지 않습니까?
Karim AG

1
안녕하세요 Karim AG, 저는 둘 다라고 생각합니다. 나는 ViewData에 물건을 저장하는 것을 나쁜 습관으로 생각하는 경향이 있습니다. (추적하기 어렵고, 사전 기반이며, 실제로 입력되지는 않습니다) ...하지만 ... 강력한 유형의 개체에 모든 레이아웃 속성을 입력하는 것은 좋은 방법입니다. 그래서 저는 여기에 한 가지를 저장하고 나머지는 강력하게 형식화 된 좋은 ViewModel에 잠급니다.
BlackjacketMack

30

이것은 매우 기본적인 것입니다. 여러분이해야 할 일은 기본 뷰 모델을 만들고 모두 확인하는 것입니다! 그리고 나는 모두를 의미한다! 해당 레이아웃을 사용할 뷰 중 해당 기본 모델을 사용하는 뷰를 받게됩니다!

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

홈 컨트롤러의 인덱스 (예 :) 메소드에서 :

    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에 포함해야합니다.

그러나 기본 사용자에게는 이것이 트릭을 할 것입니다.


동의합니다. 고마워.
Sebastián Guerrero

1
위로 그냥 대신 기본 클래스의 인터페이스와 함께 작동 언급 할
VladL

27

일반적인 해결책은 레이아웃 파일에 사용 된 속성을 포함하는 기본보기 모델을 만든 다음 기본 모델에서 각 페이지에서 사용되는 모델로 상속하는 것입니다.

이 접근 방식의 문제는 이제 모델의 문제가 다른 하나의 클래스에서만 상속 할 수 있다는 문제에 잠겨 있다는 것이며, 어쨌든 의도 한 모델에서 상속을 사용할 수없는 솔루션 일 수 있습니다.

내 솔루션은 기본 뷰 모델로 시작됩니다.

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

1
다중 상속 문제를 지적하고 처리하는 방법에 대한 보너스! 이것은 확장성에 대한 더 나은 대답입니다.
Brett Spencer

1
제 생각에는 최고의 솔루션입니다. 아키텍처 관점에서 볼 때 확장 가능하고 유지 관리가 가능합니다. 이것이 올바른 방법입니다. 저는 ViewBag 나 ViewData를 좋아하지 않았습니다 ..... 둘 다 나에게 엉망인 것 같습니다.
Jonathan Alfaro

10

필요한 모델을 부분보기에 전달하는 고유 한 컨트롤러로 새 부분보기를 추가하고 마지막으로 RenderPartial 또는 RenderAction을 사용하여 Layout.cshtml에서 언급 된 부분보기를 렌더링하지 않는 이유는 무엇입니까?

이 방법을 사용하여 이름, 프로필 사진 등과 같은 로그인 한 사용자의 정보를 표시합니다.


2
이것에 대해 자세히 설명해 주시겠습니까? 이 기술을 사용하는 일부 블로그 게시물에 대한 링크를
부탁드립니다

이것은 작동 할 수 있지만 성능이 저하되는 이유는 무엇입니까? 컨트롤러가 수행 한 모든 처리를 기다렸다가 뷰를 반환해야합니다. 사용자의 브라우저가 필요한 데이터를 얻기 위해 다른 요청을하도록해야합니다. 레이아웃이 적절하게 렌더링하기 위해 데이터에 의존한다면 어떻게 될까요? IMHO 이것은이 질문에 대한 대답이 아닙니다.
Brett Spencer

3

오래된 질문이지만 MVC5 개발자를위한 솔루션을 언급하기 위해 Model보기에서와 동일한 속성을 사용할 수 있습니다.

Model보기 및 레이아웃 의 속성은 동일한 ViewDataDictionary개체와 연결되어 있으므로 모델을 레이아웃 페이지로 전달하기 위해 추가 작업을 수행 할 필요가 없으며 @model MyModelName레이아웃에서 선언 할 필요가 없습니다 .

그러나 @Model.XXX레이아웃에서 사용할 때 intelliSense 상황에 맞는 메뉴가 나타나지 않습니다. Model여기에는 ViewBag.


2

기술적으로는 그것을 처리하는 적절한 방법이 아닐 수도 있지만, 가장 간단하고 합리적인 해결책은 클래스를 만들고 레이아웃에서 인스턴스화하는 것입니다. 그렇지 않으면 올바른 방법에 대한 일회성 예외입니다. 이것이 레이아웃보다 더 많이 수행된다면, 당신이하는 일을 진지하게 재고하고 프로젝트를 더 진행하기 전에 몇 가지 튜토리얼을 더 읽어야합니다.

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

그런 다음보기에서

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

.net 코어에서는이를 건너 뛰고 종속성 주입을 사용할 수도 있습니다.

@inject My.Namespace.IMyLayoutModel myLayoutModel

그것은 일종의 그늘진 영역 중 하나입니다. 그러나 내가 여기서보고있는 매우 복잡한 대안을 감안할 때 실용성의 이름으로 만드는 것은 괜찮은 예외 이상이라고 생각합니다. 특히 단순하게 유지하고 무거운 논리 (정말로 있어서는 안되지만 요구 사항은 다르다고 주장함)가 속한 다른 클래스 / 계층에 있는지 확인하십시오. 기본적으로 하나의 뷰를 위해 모든 컨트롤러 또는 모델을 오염시키는 것보다 확실히 낫습니다.


2

보관하는 또 다른 방법이 있습니다.

  1. 모든 컨트롤러에 대해 BaseController 클래스를 구현하기 만하면 됩니다 .

  2. 에서 BaseController예를 들어 class 같은 모델 클래스를 반환하는 메서드를 만듭니다.

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>

0

모델이 개체 모음 (또는 단일 개체)이라고 가정 해 보겠습니다. 모델의 각 개체에 대해 다음을 수행합니다.

1) 표시하려는 개체를 ViewBag에 넣습니다. 예를 들면 :

  ViewBag.YourObject = yourObject;

2) 개체에 대한 클래스 정의가 포함 된 _Layout.cshtml 맨 위에 using 문을 추가합니다. 예를 들면 :

@using YourApplication.YourClasses;

3) _Layout에서 yourObject를 참조 할 때 캐스팅합니다. (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; }
}

레이아웃에서 IContainsMyModel을 사용하십시오.

해결되었습니다. 인터페이스 규칙.


1
왜 반대표를 받았는지 모르겠습니다. 여기에서 한 것과 유사한 인터페이스를 사용하는 것이 제 상황에서 작동했습니다.
costa

-6

예를 들면

@model IList<Model.User>

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

새로운 @model 지시문 에 대해 자세히 알아보기


하지만 컬렉션의 첫 번째 요소를 레이아웃 모델에 전달하려면 어떻게해야합니까?
SiberianGuy

컨트롤러의 첫 번째 요소를 가져 와서 모델을 @model Model.User로 설정해야합니다.
Martin Fabik

첫 번째 요소 만 -하지만 내 페이지가 IList의 및 레이아웃 싶어
SiberianGuy

내가 당신을 옳게 이해했다면 모델이 IList <SomeThing>이고 뷰에서 컬렉션의 첫 번째 요소를 얻으려면? 그렇다면 @ Model.First () 사용
Martin Fabik 2010

6
포스터는 레이아웃을 사용하는 메인 뷰가 아닌 _Layout.cshtml 페이지에 모델을 전달하는 방법에 대해 묻고있었습니다.
Pure.Krome 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.