WFFM Require should not attach to global scope

One of our projects uses Backbone.js extensively. However, since WFFM for Sitecore 8.1, most of backbone components either stop working
or act nondeterministically. Issue occurs because WFFM uses requirejs in global scope to load scripts asynchonously and it spoils existing solutions. I have noticed that this issue was briefly mentioned here: https://community.sitecore.net/developers/f/10/t/1535 .

Take an example:
1. Setup new Sitecore 8.1 with WFFM
2. Install MVC sample items (ctor.io/sitecore-mvc-sample-items-for-sitecore-8)
3. Add backbone & underscore scripts to sample layout.cshtml:
<script type="text/javascript" src="underscorejs.org/underscore.js"></script>
<script type="text/javascript" src="backbonejs.org/backbone-min.js"></script>
4. Go to Home item in preview
Random errors which can occur:
-Uncaught Error: Mismatched anonymous define() module: function (i,r,n){e.Backbone=t(e,n,i,r)}
-Uncaught TypeError: $scw(...).wffmForm is not a function
-Uncaught TypeError: n.widget is not a function
-Uncaught TypeError: Cannot set property 'unobtrusive' of undefined
and at the same time:
Uncaught TypeError: Cannot read property 'addMethod' of undefined

In most cases Backbone global object does not exist, so all of the existing scripts which are utilising Backbone are not working.
It's not the case that requirejs is bad, but forcing people to use it when they have existing and working solution is wrong.

One of the solutions could be just to build WFFM scripts in proper way, so they will not interfere with global scope. Here's my solution to the problem, which could be applied:

  1. Become familiar with the information here: http://requirejs.org/docs/faq-advanced.html
  2. Open WFFM mvc scripts folder (I've done this in Website\sitecore modules\Web\Web Forms for Marketers\mvc)
  3. Configure RequireJS build (build.js file):
    ({
        paths: {
          requireLib : "./require-2.1.15",
          jquery: "./libs/jquery/jquery-2.1.3.min",
          jquery_ui: "./libs/jquery/jquery-ui-1.11.3.min",
          jquery_validate: "./libs/jquery/jquery.validate.min",
          jquery_validate_unobtrusive: "./libs/jquery/jquery.validate.unobtrusive.min",
          bootstrap: "./libs/bootstrap/bootstrap.min",
          wffm: "./wffm.min"
        },
        shim: {
          "bootstrap": {
            deps: ["jquery"]
          },
          "jquery_ui": {
            deps: ["jquery"]
          },
          "jquery_validate": {
            deps: ["jquery"]
          },
          "jquery_validate_unobtrusive": {
            deps: ["jquery", "jquery_validate"]
          },
          "wffm": {
            deps: ["jquery", "jquery_ui", "jquery_validate", "jquery_validate_unobtrusive", "bootstrap"]
        }
    },
    wrapShim : true,
    
        name: "main",
        include : ["requireLib"],
        namespace: "wffm",
        out: "main.js"
    })
  4. Run RequireJS optimizer from command line  (I've skipped minification)
    node r.js -o build.js optimize=none
  5. In Index.cshtml of WFFM (Website\Views\Form) remove
    @Html.RenderScripts(requirejs)

And that's it. I think that this (or similiar) solution should be applied to WFFM Module. It will be even nicer, when both global requirejs and self-contained requirejs scripts will be shipped, so end-user/developer could decide which version to use.

2 Replies