Question

I am using requirejs + jquery + jqueryui. I have read TONS of examples of how to do this. I think I understand the various approaches and it seems to me that my setup should be working correctly. However, I occasionally get a $.widget is not defined error in my custom modules that depend on jquery-ui. It is a pain because it is inconsistent and hard to reproduce so it is hard for me to test alternative approaches.

I am not shimming all of my jquery plugins because there are a lot. Instead, I am loading jquery with a separate requirejs call. Then, in the callback for that I load the rest of my stuff. This way I don't have to maintain a list of shims for all my jquery plugins.

For jquery-ui, I use a shim to make it depend on jquery. Then all my custom modules that use the widget factory have 'jquery-ui' in their dependency list.

In my templates...

requirejs.config({
    baseUrl: ATHLETE.siteConfig.jsBaseUrl,
    paths: {
        'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min',
        'jquery-ui': '//ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min'
    },
    shim: {
        "jquery-ui": ['jquery']
    },
    waitSeconds: 15
});

requirejs(['jquery'], function($) {
    requirejs(['site'], function() {
        requirejs(['mypage']);
    });
});

Note that I am loading site.js before mypage.js. They have some shared dependencies. In my build config, I exclude site.js from mypage.js, therefore the shared dependencies are compiled into site.js and not mypage.js. Therefore, I need to completely load site.js before loading mypage.js or else require might try to load those shared dependencies separately.

Here's a sample custom module of mine that depends on jquery-ui.

define([
    'jquery',
    'jquery-ui'
],function($) {
    $.widget('ui.viewAllSponsorsWidget', $.ui.dialog, {
        options: {
            autoOpen: false,
            dialogClass: 'view-all-sponsor-dialog-wrap',
            draggable: false,
            modal: true,
            resizable: false,
            width: 370,
            height: 400
        }
    });
});

The error $.widget is not defined is caused by the 5th line of this and similar custom modules of mine. Again, it is really inconsistent and hard to reproduce. More often than not, I do NOT get the error, even when I clear my cache. Can anyone think of a way that line 5 could possibly be getting executed before jquery-ui is fully loaded?

UPDATE Aug 16, 2013

I have been able to track this down a bit more. I created a simple module that depends on jquery and jquery-ui.

define([
    'jquery',
    'jquery-ui'
],function($) {
    console.log('$.widget is defined? ' + Boolean($.widget));
    console.log('jQuery.widget is defined? ' + Boolean(jQuery.widget));
});

The output of this is as follows:

LOG: $.widget is defined? false
LOG: jQuery.widget is defined? true

So somehow the global jQuery object has widget defined, but the copy provided to me by requirejs does not.

Was it helpful?

Solution

These answers are getting a bit old. I hope to offer a little bit of an updated answer. jQuery UI now comes with AMD support built in. You can simply get it using

bower install jquery-ui

and then in requirejs.config simply add jquery_ui to the paths{} section. No shim is required anymore.

If you want to load only the small subsection of jquery-ui that you need then bower install jquery-ui and then use the build tool that requirejs provides to create a package. Otherwise if you only try to import one individual file then each of the jquery-ui files will try to load their dependencies and you'll get things like "widget not defined" or "cannot find file widget.js" etc. I did a quick writeup here: https://hendrixski.wordpress.com/2015/04/17/adding-only-the-parts-of-jquery-ui-that-you-need-into-your-requirejs-project/

Hope that helps.

OTHER TIPS

Change your shim to this:

shim: {
    'jquery-ui': ['jquery']
}

Here is an example of a bunch of Require.js configurations for popular libraries.

had a similar issue were 3rd part ASP.NET controls were hijacking the jQuery define.

$telerik.$ = jQuery.noConflict();

Although the script cleaned itself, the tail of the jQuery script effectively overwrote my RequireJS mapping

if ( typeof define === "function" && define.amd ) { define( "jquery", [], function () { return jQuery; } ); }

RequireJS would delay loading scripts and the RadScriptManager would keep injecting scripts under the form tag so there were allot of weird race issues.

For some requests, $ was from my map and was global Other requests $ was $Telerik.$ and was not global and a different version

To resolve my issue I rolled back to the jQuery version the Telerik controls were using and hard coded it in a scrip tag to ensure it was always available and then disabled Telerik from loading its own JQuery. Not AMD but works good enough for me.

I think I have a solution to my problem. Though since this problem has been hard to reproduce, I'm going to wait a while before I "accept" my own answer as correct. Also, my answer does not load jquery using requirejs. I don't see any reason why this matters to me. But if anyone sees a way to accomplish this without having to load jquery in a separate script tag, let me know.

Since jQuery defines itself as a named AMD module, I can simply load it synchronously first using a standard script tag before I do any requirejs stuff. Then I also remove my paths entry for jquery, since it's already loaded under the name 'jquery' by this point. By doing this, requirejs returns the correct "copy" of jQuery to me and everything works. I also need to use jQuery.noConflict because I use Garmin Communicator which depends on Prototype. So, here's my updated code, omitting irrelevant parts.

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">$.noConflict();</script>
<script type="text/javascript">
requirejs.config({
    baseUrl: ATHLETE.siteConfig.jsBaseUrl,
    paths: {
        'jquery-ui': '//ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min'
    },
    shim: {
        "jquery-ui": ['jquery']
    },
    waitSeconds: 15
});
requirejs(['mymodule']);
</script>

And my modules can still depend on jquery and jquery-ui as usual:

define([
    'jquery',
    'jquery-ui'
],function($) {
    $.widget('ui.mycoolwidget', ...);
});

if you want to override some method of jQuery-ui , then the best is use Requirejs init method , this makes sure that in each module, which include jQuery-ui use same modified code.

use your Shim like this

  'jquery-ui': { deps: ['jQuery'], init: function ($) {

  //do whatever with $.widget , all reference of 
  // jQuery-ui is accessible here, override 


 return this;   //very imp , so all your module will use your 
                // modified jQuery-UI reference

 } },

I encountered a similar, very occasional error when using jQuery UI with Require.js that I fixed by using the jqueryui-amd conversion script to create "AMD-wrapped modules for jQuery UI for use in loaders like RequireJS".

  1. Install using npm.

    npm install -g jqueryui-amd
    
  2. Convert the jQuery UI JavaScript files as downloaded from jqueryui.com

    jqueryui-amd path/to/jquery-ui-version
    
  3. Then add the path of the new converted jqueryui directory to your require.js config file.

    requirejs.config({
        paths: {
            jqueryui: 'path/to/jquery-ui-version/jqueryui'
        }
    });
    
  4. Finally, in your JavaScript file where you are using one of the jQuery UI components.

    define(['jquery', 'jqueryui/widget', 'jqueryui/button'], function ($) {
        //Use widget and button in here, off of the given $ variable.
    });
    

I had the same problem as you, defining my widgets within RequireJS modules, and even though I am not 100% sure this is the best solution, it was one that worked for me without violating the integrity of the modules:

requirejs.config({
     //By default load any module IDs from js/lib
        baseUrl: 'js',
        shim: {
            'jquery': {
                exports: 'jQuery'
            },
            'jquery-ui': {
                deps: ['jquery'],
                exports: '$'
            }
        }
  });

When you require jquery-ui in your module, jQuery will be auto-required and the $ object will have everything you need.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top