Unable to put raw html in Script.Head()

Topics: Core, Customizing Orchard, General, Troubleshooting, Writing modules
Jan 19, 2012 at 6:42 AM

For my widget, I am constructing page head info in the driver class like this:

 

namespace MyApp.Drivers
{
    public class MyAppWidgetDriver : ContentPartDriver<MyAppWidgetPart>
    {
        // GET
        protected override DriverResult Display(MyAppWidgetPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_MyAppWidget",
                () => shapeHelper.Parts_MyAppWidget(
                    MyAppHeader: "<meta.../><link.../><script.../>"));
        }
    }
}

 

Then in my view I try to do this:

 

@using Orchard.Utility.Extensions;
@using (Script.Head())
{
    Html.Raw(@Model.MyAppHeader);
}

This renders a blank area though. When I remove the Html.Raw, the value is encoded like "&lt;meta...&gt;&lt; ink.../&gt;&lt; script.../&gt;". Is this a bug in "Script.Head()", or maybe there is another way to write to the head of the page?

 

<meta.../><link.../><script.../>
Coordinator
Jan 19, 2012 at 6:48 AM

Not supported. There are APIs specifically to put meta, link and script tags in the head. What's in head are shared resources, it requires special handling.

Jan 19, 2012 at 1:59 PM

That's a bummer. Initially I was able to put links, scripts, and code in the view just like this:

@using (Script.Head())
{
        <link href="@Model.AppUrl/lib/@Model.AppVersion/resources/css/all.css" rel="stylesheet" type="text/css" />
        <link href="@Model.AppUrl/theme/@Model.Theme/styles.css" rel="stylesheet" type="text/css" />
        if (!String.IsNullOrEmpty(@Model.ThemePath.ToString())) { 
            <link href="@Model.ThemePath/styles.css" rel="stylesheet" type="text/css" />
        }
        <script src="@Html.Raw(Model.JsInz)" type="text/javascript"></script>
        <script type="text/javascript">
            Stat.APP_FOLDER = '@Html.Raw(Model.AppFolder)';
            Stat.VIEWPORT_RENDER_TO = '@Model.RenderTo';
            Stat.VIEWPORT_WIDTH = @Model.Width;
            @if (@Model.ItemId > 0) {
                @:Stat.ITEM_ID = @Model.ItemId;
            }
        </script>
        <script src="@Html.Raw(Model.AppScript)" type="text/javascript"></script>
}
It is for a Javascript application I am "pasting" onto my view. But since I can do all this in the view, I put the logic in the controller and was hoping to send it to the view to place in the head. I tried creating static methods in the driver than calling those methods from the view, but still no luck. Any ideas or possible ways on how to accomplish the above from the driver?

Jan 19, 2012 at 2:58 PM

From the view you just do:

@{

Script.Include("myscript.js").AtHead();

Style.Include("mystyle.css").AtHead();

}

And the resources are loaded from your Scripts or Styles folders respectively.

If you want to include resources from other modules, you can give them a named key using a resources provider, see the jQuery module for how this is done. Then Scripe.Require("NamedKey") will load it.

Jan 19, 2012 at 3:12 PM

Thanks all, but I guess I have one of those unique cases. I have a static method that constructs a string for putting in the head based on some criteria. There are many conditional links, scripts, and Javascript code that gets output. The idea behind this static method is simply to place this string in the head and your done.

So I have adjusted the static method to do this in the view:

@using (Script.Head())
{
    var ebHead = @Model.EbHead as EbWidgetDriver.EbHead;
    
    foreach (var item in ebHead.Values)
    {
        switch (item.Value)
        {
            case EbWidgetDriver.EbHead.ValueTypes.Style:
                <link href="@Html.Raw(item.Key)" type="text/css" rel="stylesheet" />
                break;
            case EbWidgetDriver.EbHead.ValueTypes.Script:
                <script src="@Html.Raw(item.Key)" type="text/javascript"></script>
                break;
            case EbWidgetDriver.EbHead.ValueTypes.Code:
                <script type="text/javascript">
                    @Html.Raw(item.Key)
                </script>
                break;
        }
    }
}
Although this complicated that logic in my static method, the above works.

Jan 19, 2012 at 3:29 PM

You might be better off doing it from your controller where you can inject the IResourceManager and it has methods for adding includes and literals. But I guess if you have a working solution why change it!

Jan 19, 2012 at 3:48 PM

This was my first instinct to look at the ResourceManager to see what I can use. It seems I can only include files or register scripts, but nothing for registering a string in the head. I was hoping for something like ResourceManager.RegisterHeadString instead of only ResourceManager.RegisterHeadScript.

Jan 19, 2012 at 3:52 PM

ResourceManager.RegisterHeadScript is what you're looking for. The other methods Include and Require are for including and requiring external scripts.

Jan 19, 2012 at 4:11 PM

Oh wow you are right and it's absolutely beautiful!!! I was able to get it done by injecting the ResourceManager as you suggest:

    public class MyAppWidgetDriver : ContentPartDriver<MyAppWidgetPart>
    {
        protected IResourceManager ResourceManager { get; set; }
        
        public MyAppWidgetDriver(IResourceManager resourceManager)
        {
            this.ResourceManager = resourceManager;
        }

        // GET
        protected override DriverResult Display(MyAppWidgetPart part, string displayType, dynamic shapeHelper)
        {
            var appHead = GetMyAppHead(
                part.BaseServiceUrl,
                part.AppUrl,
                part.AppVersion);

            this.ResourceManager.RegisterHeadScript(appHead);
            
            return ContentShape("Parts_MyAppWidget",
                () => shapeHelper.Parts_MyAppWidget());
        }
}
Thanks a mil and also to the Orchard team for using MVC :)