Loading jQuery plugins using RequireJS -- jQuery undefined intermittently even after specifying deps? - javascript

I am attempting to load jQuery.jstree through RequireJS. You can see the exact source of the plugin here: https://gist.github.com/MeoMix/7882144
As I understand it, jQuery.jstree has three dependecies: jQuery, jQuery UI, and jQuery.cookie.
I begin with by loading my RequireJS shim config and then call an initial 'require' to kick things off:
require.config({
// Set the base URL to the Scripts directory of CSWeb
baseUrl: '/csweb/Scripts/',
shim: {
'jquery-ui': ['jquery'],
'jquery.jstree': ['jquery', 'jquery-ui', 'jquery.cookie'],
'jquery.cookie': ['jquery']
}
});
Here, I define my base URL relative to the root of my JavaScript files. The file jquery.js is located at the baseUrl. I also define dependencies for both plugins. Note: I have tried playing around with more explicit shim declarations including defining exports. I noticed no difference in effect.
After defining my config, I call:
require([
'jquery',
'jquery-ui',
'jquery.cookie',
'jstree/jquery.jstree'
], function () {
'use strict';
});
Doing so yields an error intermittently. Clearly an async-loading issue. The error reads:
Uncaught ReferenceError: jQuery is not defined jquery.jstree.js:978
Line 978 of jquery.jstree is simply where jQuery is passed into the closure to begin initialization of the plugin:
// 978 jquery.jstree.js: })(jQuery);
What am I not understanding here? I don't seem to experience this issue with most of my plugins. Is there something especially crappy about how this jstree plugin was written such that it is giving RequireJS fits? Or am I not understanding a core mechanic of RequireJS shim configuration?
UPDATE 1: It appears that it is something to do with the fact that I load jquery.jstree from a path. If I load another, empty file (jstree/jquery.test) -- I am able to replace the issue. However, if I then move test up a directory such that it is level with the other plugins -- it all loads fine.

The whole path you give to the shim configuration has to match the whole path of the module you require.
You name the shim jquery.jstree but you require it as jstree/jquery.jstree, so RequireJS does not use the shim and thus does not know that the plugin depends on jquery, etc. and so you get intermittent errors.
For things like plugins that I might want to use globally, I prefer to give them a well-known name that I can use throughout my application without worrying about paths. So in your case, I'd fix the problem by adding this to my config:
paths: {
"jquery.jstree": "jstree/jquery.jstree"
}
and I would then require it jquery.jstree.

Related

jQuery noconflict, bootstrap and requirejs

The environment I'm developing in is using jQuery 1.7.1 while I need to use specifically jQuery 2.1.3. But when I do use 2.1.3 some legacy code breaks (as its using 1.7.1) so I had to use jQuery.noconflict on 2.1.3
jquery-private.js:
define(["jquery"], function(jQuery){
return jQuery.noConflict(true);
});
so then inside my requirejs config file I have this:
config.js:
requirejs.config({
baseUrl: '../',
paths: {
'jquery': 'js/jquery/jquery',
'jqueryui': 'js/jquery-ui/jquery-ui',
'bootstrap': 'js/bootstrap',
},
map: {
// '*' means all modules will get 'jquery-private'
// for their 'jquery' dependency.
'*': {
'jquery': 'jquery-private'
},
// 'jquery-private' wants the real jQuery module
// though. If this line was not here, there would
// be an unresolvable cyclic dependency.
'jquery-private': {
'jquery': 'jquery'
}
},
shim: {
'bootstrap': {
deps: ['jquery']
}
}
});
so then when I require bootstrap
widget.js:
require([
"jquery",
"bootstrap"
], function(jqr){
// SOME CODE
});
I get an error:
Uncaught Error: Bootstrap's JavaScript requires jQuery version 1.9.1 or higher
So bootstrap is picking up 1.7.1.
How can I make it pickup 2.1.3 without making changes to bootstrap.js file?
Why it is failing?
Your current attempt cannot work because your jquery-private setup ensures that as soon as jQuery is loaded as a RequireJS module, noConflict() is called. So long as the code loaded with RequireJS is referring to jQuery through a define call (e.g. define(['jquery'], function ($) {) there is no problem. However, any code that is shimmed will refer to the $ or jQuery globals and thus will refer version 1.7.1.
A Solution
Absent any other constraints, the preferred solution is to have the entire code base use the same jQuery version. Upgrade 1.7.1 to the latest in the 1.x series, and use that. According to the download page, there is no API difference between 2.x and a relatively recent release in the 1.x series (I would say 1.9.x and later). Of course, this means that the code that depended on 1.7.1 may have to be updated to work with the latest jQuery in the 1.x series.
I would have suggested upgrading to the 2.x series but you mention in comments needing compatibility with IE 6 and 7. Note here: if you do manage to get 1.7.1 and 2.1.3 to load for the same page, that part of the code that uses 2.1.3 wont' be compatible with IE 6 and 7 so the page is effectively no longer compatible with IE 6 and 7.
To use this solution, besides the changes that must be done for the upgrade, you must:
Make sure that jQuery loads before RequireJS. Otherwise, it will detect RequireJS and call define but then it won't be reliably available to code that is not loaded with RequireJS. (I said it won't be "reliably available" because although once the jquery module is loaded, then the jQuery and $ globals will exist, the problem is that code that is not loaded with RequireJS cannot wait for RequireJS to load jQuery. So, unless you take pains to write your own synchronization code, it won't work reliably.)
Create a RequireJS module which merely makes the jQuery loaded before RequireJS available to RequireJS modules as a module, so:
define('jquery', function () { return jQuery; });
This could be placed just before your call to require.config. This creates a "fake" jquery module which merely returns the global jQuery symbol. (It could just as well return $.)
Alternatives?
I do not see a robust alternative to the solution above. It is not that difficult to come up with a proof of concept that would demonstrate another approach. However, it would fail as soon as you try to use it on a real project. The problem is that RequireJS is inherently asynchronous, so you cannot start messing with the $ and jQuery globals to set them the way you want and be sure that everything you want to happen will happen at the time you want it to happen. Moreover, any code loaded before RequireJS that happens to initiate asynchronous operations that could happen interleaved with RequireJS' module loading would throw a monkey wrench into the whole operation. I'd rather refrain from proposing solutions that will fail as soon as they go from proof of concept to real world applications.

requirejs export global variable

I am trying to use bootbox (http://bootboxjs.com) with requirejs.
main.html
<script>
var require = {
baseUrl: 'js',
paths: {
"jquery" :['jquery-1.11.0.min'],
"bootbox" :['bootbox.min'],
"bootstrap" :['//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min']
},
shim:
'bootstrap' :{deps:['jquery']},
'bootbox' :{deps:['bootstrap'],exports: 'bootbox'}
}
}
</script>
<script src="require.min.js"></script>
<script src="myscript1.js"></script>
main.js
define(['bootbox'],function(bootbox){
bootbox.alert("Hello world!"); // working properly
});
myscript1.js
require(['main'],function(){
bootbox.alert("Hello world!"); // not functioning (bootbox is not defined)
});
Normally once bootbox plugin is loaded, we can use bootbox anywhere on the page with this: bootbox.alert("Hello world!"); When using requirejs, I have to pass the bootbox variable and define it everytime when using requirejs in order to use bootbox. I have tried with "exports", but it doesn't seem help.
How can we have the bootbox variable available globally once it is loaded?
Thank you.
Here's the deal. Bootbox detects whether it is running in an AMD loader in place (like RequireJS). If so, it won't export anything into the global space but will instead define itself as an AMD module, to be loaded by an AMD loader.
Tidoo's suggestion of loading Bootbox with a script tag works, if you do it carefully. You'd have to load Bootbox before you load RequireJS, for one thing. Otherwise, it would detect that RequireJS is loaded, determine that it is running with an AMD loader, and define itself as an AMD module. This would cause a mismatched anonymous module error from RequireJS. Another thing is that you'd have to load jQuery outside RequireJS and load it before Bootbox. So that's another script tag.
I'd suggest avoiding that solution by simply modifying your code to add the required dependency:
require(['bootbox', 'main'],function(bootbox){
bootbox.alert("Hello world!");
});
Also, you must not use a shim configuration for Bootbox because it detects whether there is an AMD loader around and calls define by itself if it detects one. A shim configuration is needed only for code that does not call define.
If you want Bootbox to be available globally you should include it manually with a script tag. RequireJS doesn't expose it.
However, I highly recommend against this. The whole idea of dependency frameworks such as RequireJS is that you don't define anything on global scope, but to make your dependencies explicit. This not only allows you to lazy load scripts, but it also makes it possible to later replace dependencies, e.g. for unit testing.
EDIT:
As Louis correctly mentioned Bootbox already has AMD support. If it detects RequireJS (or any other AMD loader) it doesn't expose anything to the global scope, but since it has AMD support you also don't need to shim it.

RequireJs optimizer ignore plugin

I would like to ignore the use of a require js plugin when I use the optimizer
define(["css!styles.css"])
This always gives me this error
Cannot read property 'normalize' of undefined.
I've set this options to the require optimizer
{
paths : {
'css' : 'empty:'
}
}
But it keeps giving me the error.
I don't know if that is what you want, but you could stub out the css plugin.
//Specify modules to stub out in the optimized file. The optimizer will
//use the source version of these modules for dependency tracing and for
//plugin use, but when writing the text into an optimized bundle, these
//modules will get the following text instead:
//If the module is used as a plugin:
// define({load: function(id){throw new Error("Dynamic load not allowed: " + id);}});
//If just a plain module:
// define({});
//This is useful particularly for plugins that inline all their resources
//and use the default module resolution behavior (do *not* implement the
//normalize() method). In those cases, an AMD loader just needs to know
//that the module has a definition. These small stubs can be used instead of
//including the full source for a plugin.
stubModules: ['text', 'bar'],
So in your case:
stubModules: ['css']
More details, see Requirejs Optimizer Config options

Using RequireJS to load a module and all its plugins

Is there a way to do
require ['jquery'], ($) ->
...
instead of
require ['jquery', 'jquery-cookies', 'jquery-scroll', 'jquery-foo', jquery'bar'], ($) ->
...
each time?
One option would be to just define a new module that is jquery with your plugins.
// new module in something like jqueryBundle.js
define(['jquery', 'jquery-cookies', 'jquery-scroll', 'jquery-foo', 'jquery-bar'], function($) {
return $;
});
Then you could just require jqueryBundle instead.
Edit: Per comment suggestion, I neglected to mention that my answer expected the jquery plugins to be either amd modules (by author or wrapped by you) or already configured via shim configuration: http://requirejs.org/docs/api.html#config-shim
Many jquery plugins are not AMD modules, but are usually great candidates for simple shim configuration. For a small sample see my answer to another question: Using require.js with Twitter Boostrap API and backbone

Loading jQuery, Underscore and Backbone using RequireJS 2.0.1 and shim

I am experimenting a little bit with RequireJS 2.0.1. My goal is to load correctly jQuery, Underscore and Backbone. From the original RequireJS doc I discovered that the author J. Burke added (to this new release) a new config option called shim.
Then I wrote this stuff down here:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Testing time</title>
<script data-main="scripts/main" src="scripts/require.js"></script>
</head>
<body>
<h1>Testing time</h1>
</body>
</html>
scripts/main.js
requirejs.config({
shim: {
'libs/jquery': {
exports: '$'
},
'libs/underscore': {
exports: '_'
},
'libs/backbone': {
deps: ['libs/underscore', 'libs/jquery'],
exports: 'Backbone'
}
}
});
define(
['libs/jquery', 'libs/underscore', 'libs/backbone'],
function (jQueryLocal, underscoreLocal, backboneLocal) {
console.log('local', jQueryLocal);
console.log('local', underscoreLocal);
console.log('local', backboneLocal);
console.log('global', $);
console.log('global', _);
console.log('global', Backbone);
}
);
Everything seems to work quite fine, but I have the feeling that I'm missing something, I know that there are AMDed version of jQuery and Underscore but if the setup is so simple I don't understand why I should use them.
So, is this setup right or I'm missing something?
You only need to use "shim" config if the library does not already call define() to declare a module. jQuery does this already, so you can remove that from the shim config. The above code will work as is, but the exports shim config for jQuery will be ignored since jQuery will call define() before the shim work is done.
The downsides with the shim vs having the script call define() to define a module:
It is less portable/reliable: every developer needs to do the shim config, and keep track of library changes. If the library author does it inline in the library, everyone gets the benefits more efficiently. The code templates at umdjs/umd can help with that code change.
Less optimal code loading: shim config works by loading shim deps before loading the actual library. So it is a bit more sequential loading than parallel. It is faster if all scripts can be loaded in parallel, which is possible when define() is used. If you do a build/optimization for final deployment and combine all the scripts into one script, then this is not really a downside.
What you are doing is correct, but jQuery does not need to be in the shim config because it exports an AMD (Asynchronous Module Definition) module. Underscore removed its support for AMD / Require.js quickly after adding it, see: Why underscore.js removed support for AMD
Shim is intended as a convenience for using libraries that do not export an AMD module. If the library you are using does support AMD, or has 2 versions (one that supports AMD, and one that is a global variable) you should use the AMD version. You should use the AMD version for the same reasons you would use AMD in the first place and also because the library may include require.js (or Almond ) in its source and would be adding unnecessary file size to your project.
Can you actually avoid "shimming" jquery in the original example (where the path to jquery is set to 'libs/jquery'), since jquery adds the name "jquery" in their amd module definition?
define( "jquery", [], function () { return jQuery; } );
My experience is that you need to put jquery.js in the baseurl directory to get the jquery amd module defined as expected, or "shim" it like in the original example.

Categories

Resources