Getting started with RequireJS for ASP.NET MVC

RequireJS.NET components are compatible with ASP.NET MVC v3 or newer and are available for download as nuget packages on Nuget.org.

Download

To install RequireJS.NET, run the following command in the Package Manager Console:
PM> Install-Package RequireJsNet

Optionally you can download the RequireJS scripts bundle that contains require.js, the text and i18n plug-ins:
PM> Install-Package RequireJsNet.Scripts

If you need bundling support you'll have to install RequireJS.NET Compressor package as well:
PM> Install-Package RequireJsNet.Compressor

If you need internationalization support install RequireJS.NET ResxToJs package:
PM> Install-Package RequireJsNet.ResxToJs

If you are currently using RequireJS.NET v1.x be aware that upgrading to v2 introduced breaking changes, please follow the upgrade guide.

JS Code Structure

If you're writing JavaScript code inside Razor views it's time to stop. With RequireJS.NET you can have a JavaScript file counterpart for each view, a JS module (like a C# class) that can be compressed and bundled with its dependencies (like IL Merge does).

For each ActionResult that returns a view, you'll need to create a js file named the same as the action itself. The requirejs entrypoint for each page will be loaded from ~/Scripts/Controllers/{area}/{controller}/{action}. If the action you're rendering is not in an area, replace {area} with Root.

This is how a bare-bones AMD module corresponding to the Home/Index action looks, it just loads the bootstrap js and it's dependencies:

require([
        'jquery',
        'bootstrap'
], function ($) {

    var indexScript = function () {
        this.init();
    };

    indexScript.prototype.init = function () {
        //do stuff with bootstrap
    };

    //create object on DOM ready
    $(function () {
        var entryPoint = new indexScript();
    });
});

If you want to develop reusable components shared between code-behind modules, use the define keyword and name your AMD module, naming it will let you reference it in other modules:

define('menu-module', [
    'jquery',
    'bootstrap'
],(function ($) {

    //constructor
    var menuScript = function() {
    };

    //public method
    menuScript.prototype.init = function () {
        $('.navbar-brand').text('RequireJS.NET');
    };

    //public module
    return menuScript;
}));

In order to use the menu-module in Home/Index js code, you'll have to add it as a dependency and pass it as a parameter to the anonymous function:

require([
        'menu-module',
        'jquery',
        'bootstrap'
], function (menu, $) {

    var indexScript = function () {
	
	//create menu object
	mainMenu = new menu();

	//call menu methods
	mainMenu.init();
    };

    $(function () {
        var entryPoint = new indexScript();
    });
});
Configure paths and dependencies

There are two formats currently supported for configuration files: JSON and XML. XML is deprecated since v2.0 and will be removed in v3.0. If you don't specify any configuration when rendering, the default will be loaded from ~/RequireJS.json. The configuration format is compatible with require.js api and it's composed of paths, shim, map, bundles and autoBundles, the last one is used by RequireJS Compressor. The modules included in auto bundles are scanned by the Compressor, bundled together with their dependencies and minified with YUI Compressor for .Net.

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. 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.

Supposing you have installed RequireJS.Scripts package, bootstrap and jquery, this is how a RequireJS.json file should look like:

{
    "paths": {
        "jquery": "jquery-1.10.2",
        "jquery-validate": "jquery.validate",
        "jquery-validate-unobtrusive": "jquery.validate.unobtrusive",
        "bootstrap": "bootstrap",
        "respond": "respond",
        "i18n": "Components/RequireJS/i18n",
        "text": "Components/RequireJS/text",
        "menu-module" : "Controllers/Common/menu-module"
    },
    "shim": {
        "jquery-validate": {
            "deps": [ "jquery" ]
        },
        "jquery-validate-unobtrusive": {
            "deps": [ "jquery", "jquery-validate" ]
        },
        "bootstrap": { 
            "deps":  ["jquery"]
        }
    },
    "autoBundles": {
        "main-app": {
            "outputPath": "Scripts/Bundles/",
            "include": [
                {
                    "directory": "Controllers/Root"
                }
            ]
        },
        "require-plugins": {
            "outputPath": "Scripts/Bundles/",
            "include": [
                {
                    "file": "Components/RequireJS/i18n"
                },
                {
                    "file": "Components/RequireJS/text"
                }
            ]
        }
    }
}
Render Requirejs config in layout

Rendering is done using the RenderRequireJsSetup html helper. Generally you want to place this in all layout files you're using, before you close the body tag.

@using RequireJsNet

<!DOCTYPE html>
<html>
<head>
<!-- head content -->
</head>
<body>
<!-- body content -->

@Html.RenderRequireJsSetup(new RequireRendererConfiguration
{
// the url from where require.js will be loaded
RequireJsUrl = Url.Content("~/Scripts/Components/RequireJS/require.js"),
// baseUrl to be passed to require.js, will be used when composing urls for scripts
BaseUrl = Url.Content("~/Scripts/"),
// a list of all the configuration files you want to load
ConfigurationFiles = new[] { "~/RequireJS.json" },
// root folder for your js controllers, will be used for composing paths to entrypoint
EntryPointRoot = "~/Scripts/",
// whether we should load overrides or not, used for autoBundles, disabled on debug mode
LoadOverrides = !HttpContext.Current.IsDebuggingEnabled,
// compute the value you want locale to have, used for i18n
LocaleSelector = html => System.Threading.Thread.CurrentThread.CurrentUICulture.Name.Split('-')[0],
// store config in aspnet cache to avoid IO operations at each request
ConfigCachingPolicy = ConfigCachingPolicy.Permanent,
CacheConfigObject = true,
// instance of IRequireJsLogger
Logger = null,
// extensability point for the config object
ProcessConfig = config => { },
// extensability point for the options object
ProcessOptions = options => { },
// value for urlArgs to be passed to require.js, used for versioning
UrlArgs = RenderHelper.RenderAppVersion()
})

</body>
</html>

You can use RequireRendererConfiguration.UrlArgs to append the assembly version value to each of the script urls for cache busting.

public static class RenderHelper
{
	public static string RenderAppVersion()
	{
		return "v=" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
	}
}
Passing values between ASP.NET and JavaScript

There are two collections available that you can use for passing various settings between your application and the front-end. You can modify these collections using RequireJsOptions.Add. There are two scopes available, global (the values will be found in requireConfig.websiteOptions) and page (the values will be found in requireConfig.pageOptions). It is often useful to have some values that don't change passed to the front-end. To accomplish this, you can call the RequireJsOptions.Add method from a custom filter. Please note that the collections are stored in the current request's Items hashset, so attempting to set any options outside of a thread that has a request attached will result in an exception.

Set global options using an action filter:

public class RequireOptionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var url = new UrlHelper(filterContext.RequestContext);
        RequireJsOptions.Add("globalUrlViaFilter", url.Action("Index", "Contact"), RequireJsOptionsScope.Global);
    }
}

Set page options inside an action:

public ActionResult Index()
{
    RequireJsOptions.Add("ids", new[] { 1, 2, 3 }, RequireJsOptionsScope.Page);

    return View();
}

Read both options inside the js code-behind module:

require([
        'bootstrap'
], function () {

    var indexPage = function () {
        this.init();
    };

    indexPage.prototype.init = function () {

        //read global options sent by RequireOptionFilter
        console.log("Website Options: " + requireConfig.websiteOptions.globalUrlViaFilter);

        //read page options, sent by HomeController.Index
        console.log("Page Options: " + requireConfig.pageOptions.ids);
    };

    $(function () {
        var entryPoint = new indexPage();
    });
});
MVC partial views

Writing JS code inside partial views is bad practice, you'll end up by having a view that's composed of several slices of HTML and JS code making debugging, compression and caching very difficult.

The JS code for the partial view can reside in a dedicated JS file (an AMD module), that can be referenced in the JS module of the view that contains the partial one.

App/
   └── Views/
   │   ├── Account/
   │   │   └── Index.cshtml
   │   └── Shared/
   │       ├── _Login.cshtml
   │       └── _Register.cshtml
   │	
   └── Scripts/Controllers/Root/
         ├── Account/
         │   └── index.js
         └── Shared/
             ├── login-module.js
             └── register-module.js

login-module.js (the register-model.js has the same code structure)

define('login-module', [
    'jquery'
],(function ($) {

    var loginModule = function() {
        //constructor logic
    };

    loginModule.prototype.init = function () {
        //login init logic
    };
	
    loginModule.prototype.validate = function () {
        //validation logic
    };
	
    return loginModule;
}));

Add the two modules to the configuration file in the paths section, this 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.

RequireJS.json

{
    "paths": {
        "login-module" : "Controllers/Root/Shared/login-module",
        "register-module" : "Controllers/Root/Shared/login-register",
        ...
    },
    "shim": {
        ...
    },
    "autoBundles": {
        ...
    }
}

Reference and use the two modules in the Index js file:

Index.js

require([
        'login-module',
        'register-module'
], function (loginModule, registerModule) {

    var indexModule = function () {
        //login and register scripts are loaded as this point
        //create the login and register objects and call theirs methods
		
        var login = new loginModule();
        login.init();
		
        var register = new registerModule();
        register.init();
    };

    //create object on DOM ready
    $(function () {
        var entryPoint = new indexModule();
    });
});
Overriding entrypoint resolution

You have the ability to fully customize the way in which the entry point is generated.

In order to do so, you need to add to the RequireJsOptions.ResolverCollection collection (or replace its contents fully if you wish)

When the view is rendered, we iterate through the resolver collection and stop at the first one that returns a non-null result

The collection contains instances of IEntryPointResolver, so you can customize the behavior in any way you like.

		
public class FallbackEntryPointResolver : IEntryPointResolver 
{
     public string Resolve(ViewContext viewContext, string entryPointRoot)
     {
	// compute this any way you want
           return "default";
     }
}
		
Source code

A working example with the above code is hosted on GihHub at RequireJsNet.Samples. If you have suggestion or any kind of feedback regarding RequireJS.NET project please submit an issue here.