0

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?

WPRookie82
  • 165
  • 9
  • The `@section` is not shown cause the PartialView doesn't have a Layout page. Have you tried to call the partial view directly from the Layout? – Borka Jun 10 '20 at 18:05

1 Answers1

1

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>
Borka
  • 803
  • 5
  • 16
  • I thought of this solution but like this I have to call the database multiple times, right? PS: I kept my post short but in reality I have HeadStart, HeadEnd, BodyStart and BodyEnd so four separate DB calls. – WPRookie82 Jun 10 '20 at 18:16
  • Well, basically yes, if you can't somehow merge them somehow. Another thing i can think of is creating a `BaseController`, load all your scripts there and put them inside of a `ViewBag` and call the `ViewBag` in the Layout. Ill update my answer with that as well – Borka Jun 10 '20 at 18:19
  • @WPRookie82 check the updated answer, if that works for you – Borka Jun 10 '20 at 18:29
  • @WPRookie82 glad i could help :D – Borka Jun 10 '20 at 18:52