2

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?

Wil
  • 10,234
  • 12
  • 54
  • 81
  • Perhaps [child actions](http://stackoverflow.com/questions/12530016/what-is-an-mvc-child-action). You can also OutputCache these if you reuse the same data often. – Jasen Nov 05 '15 at 07:27
  • @Jasen - Thanks, that looks very interesting... I'm going to try to implement now. Please can you write that up as an answer as unless something really screws up/I can't implement or there is a much better answer, I think that is the way to go here. – Wil Nov 05 '15 at 07:54

1 Answers1

2

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" })}
}
Community
  • 1
  • 1
Jasen
  • 14,030
  • 3
  • 51
  • 68
  • The one thing I was most scared about was having dynamic elements in the view and requiring to get the elements in a new ViewModel on every single page... this way of doing it, combined with output caching (which I had no idea about before) is just perfect and exactly what I wanted and what I needed. – Wil Nov 06 '15 at 20:47
  • p.s. I set cache to only 5 minutes, and as the update will happen less than once per 2 months, it really isn't an issue... but... is there a way to programatically expire the cache that I can add to the part of the code that updates the items? – Wil Nov 06 '15 at 20:48
  • 1
    [Here's](http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/improving-performance-with-output-caching-cs) some good info on OutputCache. [Here's a SO answer](http://stackoverflow.com/questions/16194140/how-to-invalidate-cache-data-outputcache-from-a-controller) regarding invalidating the cache. – Jasen Nov 07 '15 at 01:31
  • Thanks! Just what I needed – Wil Nov 08 '15 at 22:07