Search code examples
c#asp.net-mvcmodelentity-framework-6asp.net-mvc-viewmodel

What is the best practice of passing data from a controller to a view layout?


I currently have a MVC site that needs to have dynamic content on the header of every page.

I currently get the required data as normal in the controller and place it in a View Model. In the view, I take the data and stick the template parts in to the Viewbag and finally, on the main layout page, I take the Viewbag data and pass it to the partial which controls the header.

I've read that I shouldn't use Viewbag where possible, and the amount of times I pass the data round just doesn't feel right.

The only way I can think of to improve this is to create a section on the main layout, and then put the partial/data in the section of the view - however, there are ~30 pages and this again doesn't feel like the correct route.

Is there a better way to do this/what are the best practices for taking dynamic data that has to go to the main view?


Solution

  • You can do this with Child Actions. You can reuse this action and even include it in the _Layout page.

    Here's a child action to display some header info. It is marked ChildActionOnly so it can only be called within another view. It also takes advantage of OutputCache to save a result for 5 minutes.

    [ChildActionOnly]
    [OutputCache(Duration = 5 * 60)]
    public ActionResult Header(string section)
    {
        var info = new HeaderInfo
        {
            Section = section,
            Today = DateTime.Now
        };
        return PartialView("_header", info);
    }
    

    _header.cshtml

    @model HeaderInfo
    <div>
        <span>@Model.Section</span>
        <span>@Model.Today.ToLongTimeString()</span>
    </div>
    

    Then use this in a view or layout with Html.Action() or .RenderAction().

    @Html.Action("Header", "Home", new { section = "Cake" })
    // or
    @{Html.RenderAction("Header", "Home", new { section = "Pie" });}
    

    You can specify a section inside your layout then conditionally render if present in the view.

    _Layout.cshtml

    @RenderSection("header", required: false)
    

    main view

    @section header {
        @{Html.RenderAction("Header", "Home", new { section = "Cake" })}
    }