I'm retrieving statistics information from the database in the form and shape of <script>
tags for all my Views, some of which will go in the <head>
, others in the <body>
.
Some of this information is shared by all Views, like Google Analytics but other information is specific to certain Views only like order details which is only available in the Order Confirmation View.
The end result must be:
<head>
<script><!-- Google Analytics --></script>
</head>
I am using named sections in the Layout to achieve what I want:
<head>
@RenderSection("headScripts", required: false)
</head>
<body>
@RenderSection("bodyScripts", required: false)
</body>
A sample View code is:
@if ((Model.Scripts.Head != null) && (Model.Scripts.Head.Count() != 0))
{
<text>
@section headScripts
{
@foreach (var script in Model.Scripts.Head)
{
@Html.Raw(@script.Code);
}
}
</text>
}
All view models are inheriting from a base class with the Scripts field and the code I pasted above is replicated in all my views, which is a problem.
I tried to move the code to a PartialView, starting with this line right below the </body>
in my Layout:
@{Html.RenderAction("Index", "Scripts");}
And in my ScriptsController:
public ActionResult Index()
{
Scripts scripts = new Scripts();
scripts.Head = whatever comes from the database;
scripts.Body = whatever comes from the database;
return PartialView("/Views/Shared/scripts.cshtml", scripts);
}
Everything worked, the Model is correctly populated and available in the scripts Partial View but unfortunately @section
cannot be called in a PartialView so the <script>
tags are not displayed.
Any workaround to have @if ((Model.Scripts.Head != null) && (Model.Scripts.Head.Count() != 0))
and the rest of the code in one common place used by all Views?
Maybe do it like this
<head>
@RenderSection("headScripts", required: false)
@Html.RenderAction("HeadScripts", "Scripts")
</head>
<body>
@RenderSection("bodyScripts", required: false)
@Html.RenderAction("BodyScripts", "Scripts")
</body>
Then in your scripts controller you would have two methods for each call
public ActionResult HeadScripts()
{
Scripts scripts = new Scripts();
scripts.Head = whatever comes from the database;
return PartialView("/Views/Shared/scripts.cshtml", scripts);
}
public ActionResult BodyScripts()
{
Scripts scripts = new Scripts();
scripts.Body = whatever comes from the database;
return PartialView("/Views/Shared/scripts.cshtml", scripts);
}
Hope this helps
EDIT:
Also in the PartialView you won't need the @section
anymore.
@if ((Model.Scripts.Head != null) && (Model.Scripts.Head.Count() != 0))
{
@foreach (var script in Model.Scripts.Head)
{
@Html.Raw(@script.Code);
}
}
EDIT 2:
Using a BaseController
with a ViewBag
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewBag.HeadStart = whatever comes from the database;
ViewBag.HeadEnd = whatever comes from the database;
ViewBag.BodyStart = whatever comes from the database;
ViewBag.BodyEnd = whatever comes from the database;
}
}
Then in every controller you'll inherit from this base controller
public class HomeController : BaseController
{
// some methods here
}
And finally in the view
<head>
@if (ViewBag.HeadStart != null)
{
@foreach (var script in ViewBag.HeadStart)
{
@Html.Raw(@script.Code);
}
}
@RenderSection("headScripts", required: false)
@* do the same for end *@
</head>
<body>
@* same thing here as well *@
</body>