Manoj Kumawat
Sep 26, 2019
  2676
(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
Dynamic packages in Commerce Connect

In Optimizely Commerce Connect, you can group different items using packages and bundles. Package: A package has one or more versions of a product...

K Khan | Nov 1, 2024

Efficient Catalog Metadata Management and Product Updates Using DTOs in Optimizely Commerce

This post explores ways to manage and update catalog metadata in Optimizely Commerce by utilizing Data Transfer Objects (DTOs). DTOs provide a...

Sujit Senapati | Oct 31, 2024

Effortlessly Resize Images with Cloudflare's On-the-Fly Solution

Resizing images in C# has traditionally been a complex and time-consuming task, often requiring intricate code and handling various image processin...

Manoj Kumawat | Oct 31, 2024 | Syndicated blog

XSS Vulnerabilities Patched with TinyMCE 6.8.4

Two different XSS vulnerabilities were fixed in the latest update of the NuGet package EPiServer.CMS.TinyMce. Update today!

Tomas Hensrud Gulla | Oct 30, 2024 | Syndicated blog

Opticon 2024 London - Key Highlights and Insights

Welcome to Onederland. Discover the key highlights from the Opticon Conference 2024 in London, including the NetSpring acquisition, advancements in...

Raghavendra Murthy | Oct 30, 2024 | Syndicated blog

Mastering clearing Cache in Optimizely CMS with ISynchronizedObjectInstanceCache & MemoryCache

In the fast-paced world of web development, efficient memory management is essential for optimizing application performance and enhancing user...

Sujit Senapati | Oct 28, 2024