[asp.net-mvc-4] 모든 페이지에 공통적 인 레이아웃으로 데이터 전달

레이아웃 페이지가있는 웹 사이트가 있습니다. 그러나이 레이아웃 페이지에는 모든 페이지 모델이 이러한 페이지 제목, 페이지 이름 및 일부 작업을 수행하는 HTML 도우미에 대한 실제 위치를 제공해야하는 데이터가 있습니다. 또한 각 페이지에는 고유 한보기 모델 속성이 있습니다.

어떻게 할 수 있습니까? 레이아웃을 입력하는 것은 나쁜 생각 인 것 같지만이 정보를 어떻게 전달합니까?



답변

각 페이지에 동일한 속성을 전달해야하는 경우 모든 뷰 모델에서 사용하는 기본 뷰 모델을 만드는 것이 좋습니다. 그러면 레이아웃 페이지에서이 기본 모델을 사용할 수 있습니다.

이 데이터 뒤에 로직이 필요한 경우 모든 컨트롤러에서 사용하는 기본 컨트롤러에이를 입력해야합니다.

여러분이 할 수있는 일이 많이 있으며, 중요한 접근 방식은 여러 곳에서 동일한 코드를 반복하지 않는 것입니다.

편집 : 아래 댓글에서 업데이트

다음은 개념을 보여주는 간단한 예입니다.

모든 뷰 모델이 상속 할 기본 뷰 모델을 만듭니다.

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


답변

레이아웃에서 면도기에 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에서이 개체를 다시 가져 와서이 강력한 형식의 개체에 계속 액세스 할 수 있습니다.


답변

아마도 이것의 주요 사용 사례는 모든 (또는 대부분의) 컨트롤러 작업에 대한보기에 기본 모델을 가져 오는 것입니다.

이를 감안할 때 저는 이러한 답변 중 몇 가지를 조합하여 사용했습니다. Colin Bacon의 답변에 대한 기본 지원입니다.

뷰로 돌아 가기 위해 뷰 모델을 채우고 있기 때문에 이것이 여전히 컨트롤러 로직이라는 것은 맞습니다. 따라서 이것을 놓을 올바른 위치는 컨트롤러에 있습니다.

우리는 레이아웃 페이지에 이것을 사용하기 때문에 모든 컨트롤러에서 이것이 일어나기를 원합니다. 레이아웃 페이지에서 렌더링되는 부분보기에 사용하고 있습니다.

우리는 또한 강력한 형식의 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에서 ViewBag에서 ViewModel을 가져옵니다.

@{
  LayoutViewModel model = (LayoutViewModel)ViewBag.LayoutViewModel;
}

<h1>@model.Title</h1>

또는…

<h1>@ViewBag.LayoutViewModel.Title</h1>

이렇게해도 페이지의 컨트롤러 또는 뷰 모델에 대한 코딩이 방해되지 않습니다.