notoriousb1t

Using RequireJS with ASP.NET 5 MVC

RequireJS worked great for our hybrid mobile apps and our Single Page Applications, but I have had trouble finding a good way to incorporate it in our ASP.NET MVC projects. I wanted to configure RequireJS in a central place, but I needed to have specific scripts for each page.

My solution was to pass the name of the module through a data attribute. This is a rundown:

Related Articles:  Transition to RequireJS, Getting Started with Razor

HelloWorld.cshtml

    @{
        ViewBag.Title = "Hello World!";
        ViewBag.Module = "page/home_helloworld";
    }
    <h2 id="home_greeter"></h2>

HelloDolly.cshtml

    @{
        ViewBag.Title = "Hello Dolly!";
        ViewBag.Module = "page/home_hellodolly";
    }
    <h2 id="home_greeter"></h2>

I created two slightly different views in my /Views/Home folder. Each file has ViewBag.Module set to the name of the JavaScript module associated with the file. Other than that, they both are nearly identical.

_Layout.csthml

    <!DOCTYPE HTML>
    <html>
    <head>
    <title>@ViewBag.Title - ASP.NET MVC RequireJS Example</title>
    </head>
    <body>
        @RenderBody()
    <script type="text/javascript"
            src="@Url.Content("~/Scripts/lib/require.min.js")"
            data-main="@Url.Content("~/Scripts/config.js")"
            data-path="@Url.Content("~/")"
            data-module="@ViewBag.Module">
        </script>
    </body>
    </html>

The _Layout file includes RequireJS and sets the data-main attribute to the location of the initial script to run when RequireJS is finished loading. This file can be named anything. I typically call this first file main or config depending on the size of the project.

Because we tend to use sub-sites, I created an attribute called data-path to hold the relative directory to the root of the web application. This is not necessary in most cases, but it can be helpful to do it by default if there is a likelihood that your web application will be published as a sub-site.

I get the Module name set by the child view and set the name to the data-module attribute on the script. This is how the page specific script name is passed to JavaScript.

Note: data-path and data-module are not official RequireJS attributes

config.js

    (function() {
        'use strict';
        //get root path and entry point from the data attributes on the script
        var $requireEl = document.querySelector('[data-main]');
        var entryPoint = $requireEl.dataset.module;
        var rootPath = $requireEl.dataset.path;
        // configure requirejs base path and library dependencies
        require.config({
            baseUrl: rootPath + 'Scripts'
        });
        // then call the entry point if specified
        if (entryPoint) {
            require([entryPoint]);
        }
    }());

RequireJS will automatically load my config.js file when it finishes loading. config.js does the following:

  • gets the script tag where we configured all of those data properties
  • gets the Module set to the data-module attribute
  • gets the root path set to the data-path attribute
  • configures the base script path for RequireJS
  • if Module was set, it "requires" it, thereby loading that file

home_helloworld.js

    define(function(require, exports, module) {
        'use strict';
        var $greeter = document.getElementById('home_greeter');
        $greeter.innerHTML = "Hello World!";
    });

home_hellodolly.js

    define(function(require, exports, module) {
        'use strict';
        var $greeter = document.getElementById('home_greeter');
        $greeter.innerHTML = "Hello Dolly!";
    });

As a result, homehelloworld.js is loaded for HelloWorld.cshtml, and homehellodolly.js is loaded for HelloDolly.cshtml.

I've tossed this up on GitHub as a sample project. Be sure to clone it and have a look at it.

Download Watch Star