Search code examples
c1-cms

Composite C1 - Using MVC RenderSections or the like


In Composite C1 I'm using Razor syntax to create my master layout. For faster loadtimes it's recommended to put your scripts just before the end body tag instead of inside the head tag. That's why I put jQuery and other scripts just before the end body tag.

When I use a Razor function with JavaScript that refers to jQuery I get an error because jQuery hasn't been loaded yet. The HTML from the Razor function is output before the jQuery script is loaded:

Uncaught ReferenceError: $ is not defined

In MVC I can use RenderSection in the master layout to accomplish this (rendering the JavaScript below my master layout scripts

@RenderSection("FooterScripts", false)

Then in my views I can define a section like this:

@section FooterScripts {
    <script type="text/javaScript">
        $(function () {
            ...
        });
    </script>
}

Which will render the HTML in the correct place in the final HTML. Is this possible to do in Composite C1? I couldn't get RenderSection to work even though Intellisence tells me it's available.


Solution

  • There's no built in way to insert html markup from a C1 function to a specific place in a layout.

    Possible ways to implement your own logic would be:

    1. Collect the scripts to be insterted in f.e. Context.Items collection, and insert them in the end.

    2. Implement some post processing logic that would move the script tags to the bottom of the page after it is rendered.

    First way is easier to implement, here's a short working example:

    C1 Function code:

    @inherits RazorFunction
    @using Composite.Examples
    
    @functions {
    }
    
    @{
        LayoutHelper.AddDelayedScript(Script()); 
    }
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    </head>
    <body>
        Inserting a script at the bottom of a page
    </body>
    </html>
    
    
    @helper Script() {
        <script type="text/javascript">
            alert("I'm inserted!");
        </script>
    }
    

    Layout code:

    ....
    
    
            @LayoutHelper.RenderDelayedScripts()
        </body>
    </html>
    

    Class LayoutHelper, defined in App_Code:

    using System.Collections.Generic;
    using System.Text;
    using System.Web;
    
    namespace Composite.Examples
    {
        public static class LayoutHelper
        {
            private const string HttpItems_Key = "delayedscripts";
    
            public static void AddDelayedScript(IHtmlString script)
            {
                var context = HttpContext.Current;
    
                lock (context.Items.SyncRoot)
                {
                    if (!context.Items.Contains(HttpItems_Key))
                    {
                        context.Items.Add(HttpItems_Key, new List<IHtmlString>());
                    }
    
                    (context.Items[HttpItems_Key] as List<IHtmlString>).Add(script);
                }
            }
    
            public static IHtmlString RenderDelayedScripts()
            {
                var context = HttpContext.Current;
    
                var sb = new StringBuilder();
    
                if (context.Items.Contains(HttpItems_Key))
                {
                    foreach (var delayedscript in context.Items[HttpItems_Key] as IEnumerable<IHtmlString>)
                    {
                        sb.Append(delayedscript.ToHtmlString());
                    }
                }
    
                return new HtmlString(sb.ToString());
            }
        }
    }