Don't miss out Virtual Happy Hour this Friday (April 26).

Try our conversational search powered by Generative AI!

Manoj Kumawat
Sep 26, 2019
  2554
(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
Solving the mystery of high memory usage

Sometimes, my work is easy, the problem could be resolved with one look (when I’m lucky enough to look at where it needs to be looked, just like th...

Quan Mai | Apr 22, 2024 | Syndicated blog

Search & Navigation reporting improvements

From version 16.1.0 there are some updates on the statistics pages: Add pagination to search phrase list Allows choosing a custom date range to get...

Phong | Apr 22, 2024

Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog