Manoj Kumawat
Sep 26, 2019
  2631
(0 votes)

Adding a requirejs block to your site

Do you love require.js? who don't!

There are some reasons:

  •  In a large application a lot of JavaScript files are needed, and each script tag needs a request.
  •  You have to put them in a same order in which they are called, i.e. File which is dependent on other should be loaded after the dependent ones.

Well that's an overview but I'm not going to talk about it. This blog post is about adding a require block from episerver itself.

What we'll be creating?

A block, A partial view and couple of requireJS configurations and nothing else..

Before beginnging to first step, Please make sure you've downloaded javascript library of require.js. You can find it here

In the end of this blog post our result would look like this -

Step 1 - Let's start with the configuration of require.js 

Begin with configuring your javascript libraries by defining their paths with require.config method. I call this file main.js and it looks something like this. Ofcourse you can remove unwanted scripts. 

require.config({
    baseUrl: "/Static",
    paths: {
        'jquery': '/Static/js/jquery',
        'jquery_validate': '/Static/js/jquery.validate',
        'jquery_validate_unobtrusive': '/Static/js/jquery.validate.unobtrusive',
        'bootstrap': '/Static/js/bootstrap',
        'utils': '/Areas/Country/Static/js/App/utils',
        'imagesloaded': '/Static/js/imagesloaded.pkgd.min',
        'jQueryBridget': '/Static/js/jquery-bridget',
        'masonry': '/Static/js/masonry.pkgd.min',
        'jquery_mask': '/Static/js/jquery.maskedinput',
        'bootstrap-dialog': '/Static/js/bootstrap-dialog',
        'bxslider':'/Areas/Country/Static/js/App/jquery.bxslider.min'
    },
    waitSeconds: 0,
    shim: {
        "jquery.validate": {
            'deps': ['jquery'],
            'exports': 'validate'
        },
        "jquery.validate.unobtrusive": {
            'deps': ['jquery', 'jquery_validate'],
            'exports': 'unobtrusive'
        },
        "bootstrap": {
            'deps': ['jquery']
        },
        "utils": {
            'deps': ['jquery']
        },
        "jQueryBridget": {
            'deps': ['jquery']
        },
        "imagesloaded": {
            'deps': ['jquery']
        },
        "masonry": {
            'deps': ['jquery', 'jQueryBridget', 'imagesloaded']
        },
        "jquery_mask": {
            'deps': ['jquery'],
            'exports': 'jQuery.fn.mask'
        },
        "bootstrap-dialog": {
            'deps': ['jquery', 'bootstrap']
        },
        "bxslider": {
            'deps': ['jquery']
        }
    }
});

Step 2 - Creating a block 

[ContentType(DisplayName = "Require Block", GroupName = "Framework", GUID = "986db6dd-9c9d-4b9c-bd83-cff05c0f7f93", Description = "Require wrapper function definition that expose jquery, bootstrap and other libraries.($).")]
    public class RequireBlock : BlockData
    {
        [UIHint(UIHint.Textarea)]
        [CultureSpecific]
        [Display(
        Name = "Script Content",
        Description = "The script block content.",
        GroupName = SystemTabNames.Content,
        Order = 10)]
        public virtual string ScriptContent { get; set; }

        [CultureSpecific]
        [Display(
        Name = "Use Jquery",
        Description = "Include jquery.js in the require definition.",
        GroupName = SystemTabNames.Content,
        Order = 20)]
        public virtual bool UseJquery { get; set; }

        [CultureSpecific]
        [Display(
        Name = "Use Bootstrap",
        Description = "Include boostrap.js in the require definition.",
        GroupName = SystemTabNames.Content,
        Order = 30)]
        public virtual bool UseBootstrap { get; set; }

        [CultureSpecific]
        [Display(
       Name = "Use Utils",
       Description = "Include utils.js in the require definition.",
       GroupName = SystemTabNames.Content,
       Order = 40)]
        public virtual bool UseUtils { get; set; }


        [CultureSpecific]
        [Display(
           Name = "Custom Libraries",
           Description = "Include custom libraries in the require definition",
           GroupName = SystemTabNames.Content,
           Order = 50)]
        [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor<RequireBlockCustomJavascriptLibrary>))]
        public virtual IList<RequireBlockCustomJavascriptLibrary> CustomLibaries { get; set; }
    }

    public class RequireBlockCustomJavascriptLibrary
    {
        public string Libary { get; set; }

        public string ExportSymbol { get; set; }
    }

    [PropertyDefinitionTypePlugIn]
    public class RequireBlockCustomJavascriptLibraryProperty : PropertyListBase<RequireBlockCustomJavascriptLibrary>
    {

    }

We are doing a couple of steps here

  • A multiline textarea that would facilitate us to write a script within
  • Property definition to use the custom class RequireBlockCustomJavascriptLibrary

Read more about PropertyDefinitionTypePlugIn

Step 3 - Create a view

@model RequireProject.Framework.Web.Models.Blocks.RequireBlock

@{ 
    List<KeyValuePair<string, string>> libraries = new List<KeyValuePair<string, string>>();

    if (Model.UseJquery == true)
    {
        libraries.Add(new KeyValuePair<string, string>("'jquery'", "$"));
    }

    if (Model.UseBootstrap == true)
    {
        libraries.Add(new KeyValuePair<string, string>("'bootstrap'", "Bootstrap"));
    }

    if (Model.UseUtils == true)
    {
        libraries.Add(new KeyValuePair<string, string>("'utils'", "Utils"));
    }

    if (Model.CustomLibaries != null && Model.CustomLibaries.Any())
    {
        foreach(var item in Model.CustomLibaries)
        {
            libraries.Add(new KeyValuePair<string, string>(string.Format("'{0}'", item.Libary), item.ExportSymbol));
        }
    }

    List<string> allKeys = (from kvp in libraries select kvp.Key).Distinct().ToList();
    string keys = string.Join(",", allKeys);

    List<string> allSymbols = new List<string>();
    foreach (string key in allKeys)
    {
        allSymbols.Add(libraries.FirstOrDefault(o => o.Key == key).Value);
    }
    string symbols = string.Join(",", allSymbols);

}
<script>
require(['/main.js'], function(){

require([@Html.Raw(keys)]
            , function (@Html.Raw(symbols)) {       
    @Html.Raw(Model.ScriptContent)
});

});
</script>

So above here,

  • We'll initially put the configuration file main.js that already has the mapping of paths where our javascript libraries are in the filesystem.
  • Combining exported script references along with the scripts which a user selects via checkbox.
  • We'll have an export symbol along with the references given within require block script array, You will see an example below

And that's done.

Usage

Now create a require block in episerver and do the followings, shown in below image -

Note that Custom Libraries would open a popup with 2 properties, first with path and the other one is the export symbol.

Output

Doing all above would render a script on your page (wherever you use the block in epi), shown example below -

<script>
require(['/main.js'], function(){

require(['jquery','https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.js']
            , function ($,masonry) {       
    console.log('testing');
});

});
</script>

Note above, Since we enabled jQuery checkbox from the page that's why it has now included the 'jquery' and the export symbol (symbol written on view).

Hope you liked it. I'm opened to your suggestion.

Have a good day!

Sep 26, 2019

Comments

Sep 26, 2019 03:35 PM

Episerver has a built in client resources system which can be modified. https://world.episerver.com/documentation/developer-guides/CMS/client-resources/ this allows blocks to request specific scripts they require and the files are auto included as part of the rendering pipeline.

Sep 26, 2019 03:43 PM

Personally I don't see the need for require JS. All JavaScript files should be minimized and bunlded with Episerver so they are in a single request to the browser, then when on the DXC you'll be utilizing a large CDN cache for the entire bundle. I also would always download anything from a CDN and put it local in my bundle as there's inherintly risks of third party CDNs https://csswizardry.com/2019/05/self-host-your-static-assets/ where as by wrapping your assets in to your own bundle you assume one JS file cached through cloudflare recuding overhead.

As all your script is loaded and in the right order require.js isn't really needed. We used to use it in our digital agency but we've been building angular.js and vue.js apps perfectly fine without it.

Just my opinion anyhow. 

Manoj Kumawat
Manoj Kumawat Sep 26, 2019 05:01 PM

Hello @Scott,

I've been using bundling as well in my project. We had a couple of pages where bundling had no greater impact on performance while comparing it with requireJs.

I just wanted to make sure this should stay on some particular pages.

Anyways, I am totally agree with you on not to use it throughout. Thankyou

Please login to comment.
Latest blogs
Creating an Optimizely CMS Addon - Adding an Editor Interface Gadget

In   Part One   of this series, I covered getting started with creating your own AddOn for Optimizely CMS 12. This covered what I consider to be an...

Mark Stott | Aug 30, 2024

Configure your own Search & Navigation timeouts

The main blog Configure your own Search & Navigation timeouts was posted for years but you need copy the code to your application. We now bring tho...

Manh Nguyen | Aug 30, 2024

Joining the Optimizely MVP Program

Andy Blyth has been honoured as an Optimizely MVP, recognising his contributions to the Optimizely community. Learn how this achievement will enhan...

Andy Blyth | Aug 29, 2024 | Syndicated blog

Welcome 2024 Summer OMVPs

Hello, Optimizely community! We are thrilled to announce and welcome the newest members to the Optimizely Most Valuable Professionals (OMVP) progra...

Patrick Lam | Aug 29, 2024

Create custom folder type in Edit Mode

Content Folders, which are located in assets pane gadget or multichannel content gadget, allow you to add any type of block or folders. But...

Grzegorz Wiecheć | Aug 28, 2024 | Syndicated blog

Creating an Optimizely AddOn - Getting Started

When Optimizely CMS 12 was launched in the summer of 2021, I created my first AddOn for Optimizely CMS and talked about some of the lessons I learn...

Mark Stott | Aug 28, 2024