Introducing RequireJS for ASP.NET MVC

This article shows you how to integrate RequireJS in a ASP.NET MVC 4 project and structure the JavaScript code in such a manner that any C# programmer can understand and use it without advanced js programming skills. RequireJS is an implementation of Asynchronous Module Definition (AMD) that provides all the tools you need to write modular JavaScript. If you are working on a large project with lots of JavaScript code, many external components and frameworks, RequireJS will help you manage the complexity of dependencies.

Writing modular JavaScript is the first step in bringing the front-end development closer to the server-side OOP, for a C# programmer, the Module Pattern applied to JavaScript means that you can emulate the .NET class behavior by having public/private members and methods. With RequireJS you can go even further, by declaring a module you can specify dependencies to other modules, the same way as you do in a .NET project when you add references to other .NET components. Before a module is loaded by the browser, RequireJS will look for the dependencies required by that module to function, it will fetch asynchronously from the server all the other modules needed and then will let the browser execute your module code.


LE: This article refers to an old version of RequireJS.NET. Please got to requirejsnet.veritech.io for the latest release and extended documentation.


Advantages of using RequireJS with Asp.Net MVC

  1. better JavaScript code reusability
  2. reliable object and dependency management
  3. suitable for large and complex applications
  4. loads JavaScript files asynchronously
  5. using the r.js tool it makes it easy to optimize page load, because it combines all dependencies into one file removing the need for you to search for dependencies manually

You can read all about RequireJS and AMD on requirejs.org

The .NET implementation has the following features:

  • JavaScript file structure integrated with the MVC structure
  • dependencies declaration and module path configuration using xml
  • JavaScript code-behind for each Razor view
  • communication from the server-side controller (c#) to js modules using .NET
  • application wide JavaScript controller
  • Ajax requests management

RequireJS for .NET file structure

The JavaScript code-behind structure is similar to the Views structure, for each action that returns a view there is a JavaScript file that contains the page specific code. On the server-side controller, in the action code, you can set and send options to the corresponding JavaScript module.

 

Configure module paths and dependencies

RequireJS.config
<configuration>
  <paths>
    <pathkey=jqueryvalue=lib/vendor/jquery/jquery />
    <pathkey=jquery-uivalue=lib/vendor/jquery/ui/jquery-ui />
    <pathkey=jquery-ui-i18nvalue=lib/vendor/jquery/ui/jquery-ui-i18n />
    <pathkey=jquery-mobilevalue=lib/vendor/jquery/mobile/jquery.mobile />
    <pathkey=jquery-validatevalue=lib/vendor/jquery/plugins/validate/jquery.validate />
    <pathkey=jquery-validate-unobtrusivevalue=lib/vendor/jquery/plugins/validate/jquery.validate.unobtrusive />
    <pathkey=amplifyvalue=lib/vendor/amplify/amplify />
    <pathkey=dependvalue=lib/vendor/requirejs/depend />
    <pathkey=contextvalue=lib/vendor/requirejs/context />
  </paths>
  <shim>
    <dependenciesfor=jquery-uiexports=“”>
        <adddependency=jquery/>
    </dependencies>
    <dependenciesfor=jquery-ui-i18nexports=“”>
      <adddependency=jquery/>
      <adddependency=jquery-ui/>
    </dependencies>
    <dependenciesfor=jquery-mobileexports=“”>
      <adddependency=jquery/>
    </dependencies>
    <dependenciesfor=jquery-validateexports=“”>
      <adddependency=jquery/>
    </dependencies>
    <dependenciesfor=jquery-validate-unobtrusiveexports=“”>
      <adddependency=jquery/>
      <adddependency=jquery-validate/>
    </dependencies>
    <dependenciesfor=amplifyexports=“”>
      <adddependency=jquery/>
    </dependencies>
  </shim>
</configuration>

The paths section is used for mapping the file location of a component to a short name, so that you don’t have to write the whole path each time you refer it in code:

//without paths config
require([
        'lib/vendor/jquery/jquery',
        'lib/vendor/amplify/amplify'
],
    function () {  }
);

//with paths config
require([
        'jquery',
        'amplify'
],
    function () {  }
);

The shim section lets you declare dependencies for components that are not programmed as RequireJS modules. When you are referring such a component, the RequireJS engine will load all the dependencies specified in the shim before executing your own code. In the shim section you can use the module short name set in the paths config.

If you create a JS module that uses Microsoft’s unobtrusive validator plugin, all the nested dependencies will be preloaded:

require([
        'jquery-validate-unobtrusive'
],
    function () {
        var myPage = function (opt) {
            
            //jquery.js, jquery.validate.js and jquery.validate.unobtrusive.js
            //are already loaded at this point
            $.validator.unobtrusive.parse($(#myForm));
        }
    }
);

Although the above code works well and RequireJS is smart enough to load all the nested dependencies, I prefer to write in the require [ … ] area all the components that get loaded with a page. So it will be easier  to find out, in what pages a js component is being used, by just searching for the ‘com_name’ in all js files from the folder ~/Scripts/Controllers.

The xml configuration file is transformed into JavaScript code by the RequireJsHtmlHelpers extension methods, the resulting code resides in the _Layout.cshtml:

_Layout.cshtml
@Html.RenderRequireJsSetup(Url.Content("~/Scripts"), Url.Content("~/Scripts/require.js"))

Using the framework

Server-side setup

  • Create an abstract controller derived from RequireJSController, usually you’ll make an abstract controller for each area in your app
  • Implement in your abstract controller the RegisterGlobalOptions method in order to send options to app-global class
  • Create a controller derived from the abstract one

In my case I have created a controller named HomeController derived from PublicController that inherits RequireJsController, because I have multiple areas in my project where I want to set different global options based on the area, in the PublicController I will set options used in all public views and on the AdminController I will set options used only in the Admin area and so on. Here is the code:

RequireJsController.cs

[RequireJsConfig]
publicabstractclassRequireJsController : Controller
{
    publicRequireJsOptions RequireJsOptions;
    protected RequireJsController()
    {
        RequireJsOptions = newRequireJsOptions(this);
    }

    publicabstractvoid RegisterGlobalOptions();
}

The RequireJsOptions object is used by the RequireJsConfigAttribute to save in ViewBag all the options needed in app-global.js and in home-index.js (code-behind for Home/Index.cshtml).

PublicController.cs
publicabstractclassPublicController : RequireJsController
{
    publicoverridevoid RegisterGlobalOptions()
    {
        RequireJsOptions.Add(
            “homeUrl”,
            Url.Action(“Index”, “Home”, new { area = “” }),
            RequireJsOptionsScope.Website
            );
        RequireJsOptions.Add(
            “adminUrl”,
            Url.Action(“Index”, “Dashboard”, new { area = “Admin” }),
            RequireJsOptionsScope.Website
            );
    }
}

Using the RegisterGlobalOptions we can send options to app-global.js, in my case homeUrl and adminUrl. Because all the controllers in the root area are derived from PublicController these options will always be available to app-global.

HomeController.cs
publicclassHomeController : PublicController
{
    publicActionResult Index()
    {
        RequireJsOptions.Add(“ids”, newList<int> { 1, 2, 3 });
        return View();
    }
}

In the HomeController you can use RequireJsOptions.Add inside the action code to send options to the home-index.js module. An option can be a string or any other .NET type as long as it gets serialized by the Newtonsoft Json convert.

Front end setup

Now you have to create the view Index.cshtml, do not refer or write JavaScript in the html code.

After the view is done, create a file named home-index.js under ~/Scripts/Controllers/Root/Home folder. Below is an example of how to declare a module and read the options send from the server.

home-index.js

require([
        'jquery',
        'app-global'
], function () {
    //constructor
    var HomeIndex = function (opt) {

        this.options = $.extend(true, {}, opt);

        //init
        this.create();
    }

    //methods
    HomeIndex.prototype = $.extend(true, HomeIndex.prototype, {

        create: function () {          
            //access the id list send by Home/Index
            var ids = this.options.ids;
        }
    });

    //create object
    $(document).ready(function () {
        //load options sent by the server-side controller
        var page = new HomeIndex(requireConfig.pageOptions);
    });

});

Here is a schematic view of the above code

 

Working with JavaScript Modules

The RequireJS for .NET project uses two kinds of modules:

  1. a reusable self-executing module like app-global using the syntax define([ ], function() { } );
  2. an anonymous module like home-index using the syntax require([ ], function() { } );

The difference between the two is that app-global returns a function and can be used (required) by other modules, the home-index class does not return an object, it’s purpose is to manage the behavior of a single web page. The module app-global will self-execute every time a web page is loaded in the browser.

app-global.js

define([
    'jquery'
], function () {

    //constructor
    var Global = function (opt) {
        this.options = $.extend(true, {}, opt);
        this.create();
    }

    Global.prototype = $.extend(true, Global.prototype, {
        create: function () {
            //app wide UI behavior in here
        }
    });

    //self-executing module
    $(document).ready(function () {
        var myModule = new Global(requireConfig.websiteOptions);
    });
    
    //reusable module
    return Global;
});

When a browser first calls your website, require.js along with all the modules required by the home page are downloaded, when the browser navigates to another page, all the scripts downloaded for the home page will be available from cache, this way app-global.js will get download once and loaded in all pages.

home-index.js

require([
        'jquery',
        'app-global'
], function () {

    //constructor
    var HomeIndex = function (opt) {
        this.options = $.extend(true, {}, opt);
        this.create();
    }

    HomeIndex.prototype = $.extend(true, HomeIndex.prototype, {
        create: function () {
            //page UI behavior in here
        }
    });

    //create object on DOM ready
    $(document).ready(function () {
        var myPage = new HomeIndex(requireConfig.pageOptions);
    });
});

On DOM ready the app-global create() function will be called before home-index create().

Using modules inside a code-behind JavaScript class

If you want a collection of JavaScript functions to be  available in your code-behind classes, you can make a module, let’s call it app-utils.js, that is similar to app-global but does not self-execute on DOM ready. This JavaScript module is the equivalent to “public static class Utils { }” in C# that contains only public static methods.

app-utils.js

define([
    'jquery'
], function () {
    return {

        removeCaps: function (str) {
            str += ;
            if (str == str.toUpperCase()) {
                return str.charAt(0) + str.substr(1).toLowerCase();
            }
            return str;
        },

        isWP: function () {
            return/Windows Phone/i.test(navigator.userAgent);
        }
    }
});

All you have to do now is add the app-utils dependency to your page and pass it as a parameter to the anonymous function, like this:

home-index.js

require([
        'app-utils',
        'app-global'

], function (utils) {
    //utils param points to the first dependency

    var Page = function (opt) {
        this.options = $.extend(true, {}, opt);
        this.create();
    }

    Page.prototype = $.extend(true, Page.prototype, {
        create: function () {
            //call functions defined in app-utils.js
            console.log(utils.removeCaps(‘TEST’));
        }
    });

    //create object on DOM ready
    $(document).ready(function () {
        var myPage = new Page(requireConfig.pageOptions);
    });
});

Using this. inside a JavaScript Module like in a C# class

JavaScript’s functional scoping makes the language very flexible, so depending on the scope, <this> refers different objects, in order to make <this> consistent in a Module, you need to use jQuery’s $.proxy every time the scope changes from Module scope to some anonymous function.

Inside a Module I want to access the options object from any function declared in my module. If, for example, I want to know the homeUrl (options set by the server controller) value inside an on click handler I cant, because calling this.options.homeUrl will return undefined, inside the handler, <this> refers to the anchor DOM object and not my module object. The solution to this problem is to wrap the handler with $.proxy and set the context back to your module, like this:

require([
        'jquery',
        'app-global'
], function () {

    //constructor
    var Page = function (opt) {

        //memebers
        this.options = $.extend(true, {}, opt);
    }

    //methods
    Page.prototype = $.extend(true, Page.prototype, {

        registerEventsWithoutProxy: function () {

            $(‘a’).on(‘click’, function (e) {
                e.preventDefault();

                //<this> refers the anchor DOM element
                $link = $(this);
                console.log($link.text());

                //<this.options> is undefined
                //the Page object is not accesible
                console.log(this.options.homeUrl);

            });
        },

        registerEventsWithProxy: function () {

            //use $.proxy in order to acces
            //the module context <this> inside the function
            $(‘a’).on(‘click’, $.proxy(function (e) {
                e.preventDefault();

                //use $(e.currentTarget) instead of $(this)
                $link = $(e.currentTarget);
                console.log($link.text());

                //<this> refers the Page object
                console.log(this.options.homeUrl);

            }, this));
        }

    });

    //init object
    $(function () {
        //load options sent by the server-side controller
        var myPage = new Page(requireConfig.pageOptions);
    });
});

You can use the same technique in writing iteration or any other procedure that involves scope changing, just use $.proxy(function() { }, this), it makes <this> consistent in your code and avoids confusion.

iterateItems: function () {

    //use $.proxy in order to acces
    //the module context <this> inside the function
    $(‘p’).each($.proxy(function(index, item) {
                
        //access the current <p> DOM object
        $item = $(item);

        //<this> refers the Page object
        console.log(this.options);

    }, this));
}

What’s next

In future articles I will write about:

  • using RequireJS and r.js tool to combine and minify modules based on build profiles
  • managing Ajax calls, making good use of amplify.request and app-global module
  • loose coupling the modules using the pub/sub pattern and amplify.publish

Project RequireJS for .NET
Version 1.0.2
Release date 26 August 2013
First release 01 September 2012


You’ll need Visual Studio 2012 and ASP.NET MVC 4 to open the project, all the JavaScript frameworks included in the project are up to date: RequireJS, Amplify, jQuery, jQuery UI, jQuery Mobile , jQuery Validation

Further reading

20 Responses to Introducing RequireJS for ASP.NET MVC
  1. Vinny Reply

    Good article have you looked into http://nuget.org/packages/microsoft.web.optimization

    • stefan Reply

      I am using Web.Optimization.StyleBundle from MVC 4 to merge CSS files, for JavaScript I think RequireJS is more suitable.

      • Blaise Reply

        Like Vinny, I have been struggling with web.optimization and require.js because that is what many online tutorials teach.

        John Papa just published a series where js files are bundled and minified using web.optimization and then reference those js with RequireJS. Have you taken a look already? (I heard that course is now free for public view.)

        I am looking forward to your next post on bundling and minification using RequireJS. It would be great if you can comment on why we should choose not to bundle js files from MVC.

        Great article. Thank you.

        • Rui Reply

          Hi! Did you see that course? I have no idea on how to implement bundling using web.optimization and reference those files from requirejs.

          Could you give some help?

  2. [...] to Vote[Del.icio.us] Introducing RequireJS for ASP.NET MVC (9/3/2012)Monday, September 03, 2012 from [..... aspdotnetmvc.com/buzz/default.aspx
  3. [...] RequireJS for .NET, so if your are not familiar with developing RequireJS modules please read this artic... stefanprodan.com/2012/09/ajax-polling-with-amplify-and-requirejs-for-asp-net-mvc
  4. Steve Reply

    Great stuff, needs a nuget package :)

  5. John Reply

    What would be the advantage of RequireJS over Cassette??

  6. Paul Reply

    Just what I have been looking for… Thanks!

  7. Marthijn Reply

    Thanks! I’m trying to implement this using TypeScript. I’m also looking forward to your future articles!

  8. Craig Riviere Reply

    Hi Stefan,

    This is a great article, thanks for taking you time to put it together.

    I’m looking to implement RequireJS into my future ASP.NET MVC site and wondered if there were any further developments or follow up articles relating to the additions you wanted to make.

    I’d just like to use this implementation in my work so wondered if there was any more added.

    Thanks again

  9. Jason Reply

    Awesome article, thanks for putting it together.

    Do you have recommendations for how I might plug basic Knockout.js functionality into this architecture?

    Thanks,
    Jason

  10. [...] on www.stefanprodan.com Share this:FacebookLinkedInGoogle +1TwitterPinterestPrintEmailLike this:Like Loa... jczraiby.wordpress.com/2013/07/01/introducing-requirejs-for-asp-net-mvc
  11. [...] an in-depth look at RequireJS.NET please checkout this article Introducing RequireJS for ASP.NET MVC. A ... stefanprodan.com/2013/08/setup-requirejs-for-asp-net-mvc-using-nuget
  12. gurmeet Reply

    so plz give me simple code of requirejs with asp.net mvc………only msg print or value pass..but simple, bcz my starting position in requirejs, so plz send code

  13. Adrian Bonnici Reply

    Hi, I just created a project and setup requireJS.Net and everything is working fine. However I cannot figure out how to minify the modules. Will there be a tutorial on this soon? Thanks

    • stefan Reply

      We are working on a new version of RequireJsNet.Compressor that makes bundling and minification a straightforward process. Expect a complete tutorial next week.

  14. Javier Reply

    Awesome article!
    Is there a way to set a start up .js file? Suppose I have a “main.js” file and I want it to be the initial app file that should be run. Where do I config this?

    Thank you very much!

    • stefan Reply

      Hello Javier, please download from Git the RequireJsNet.Docs, in there you’ll find an app-global.js file that is shared across all modules. What you need to do is add your main.js as a dependency to all your controllers, in the controller constructor call your main.js init function.

Leave a Reply

Your email address will not be published. Please enter your name, email and a comment.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>