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.
Related
I'm trying to use requireJS to manage dependencies in my first ever single-page javascript app. Having never used requireJS before, I'm struggling with something that I think is quit basic.
My project uses knockoutJS, and an addon called knockout-sortable, which provides knockout bindings for the jquery ui 'sortable' plugin. It all works fine when I just load jquery ui, knockout and everything else without requireJS.
My require main.js looks like this:
require.config({
baseUrl: '/Scripts',
paths: {
jQuery: 'jquery-2.1.3',
jQueryUI: 'jquery-ui-1.10.2',
knockout: 'knockout-3.3.0',
knockoutSortable: 'knockout-sortable',
AppViewModel: 'app/AppViewModel'
},
shim: {
"jQueryUI": {
export: "$",
deps: ['jQuery']
},
"knockoutSortable": {
export: ["ko"],
deps: ['jQuery','jQueryUI']
},
}
});
require(['jQuery', 'jQueryUI', 'knockoutSortable', 'AppViewModel'], function ($, ui, ko, AppViewModel) {
ko.applyBindings(new AppViewModel());
});
But I get an error in my javascript that states:
GET http://localhost:8020/Scripts/jquery-ui/draggable.js
require.js:166 Uncaught Error: Script error for: jquery-ui/draggable
http://requirejs.org/docs/errors.html#scripterror
require.js:1910 GET http://localhost:8020/Scripts/jquery-ui/sortable.js
require.js:166 Uncaught Error: Script error for: jquery-ui/sortable
http://requirejs.org/docs/errors.html#scripterror
I'm guessing that knockout-sortable somehow requires the dependency jquery.ui/sortable.js, but that file doesn't exist, since jquery-ui is only one file!
This page https://learn.jquery.com/jquery-ui/environments/amd/ also suggests that I should organize my jquery ui files into some kind of folder structure, but I just have one jquery ui file, so I have no idea how to do that.
By the way: I'm using ASP.NET MVC 5 in visual studio, that why my scripts are in a 'Script' folder and not in a 'JS' folder. I've updated jquery, jquery ui and knockout to the latest versions using NuGet, and I've manually upgraded knockout-sortable to version 0.11, since that was released 4 days ago and is not yet available on nuget.
Does someone know what is going on here and how to fix it?
You can get the AMD modules when installing jquery-ui with bower or download them from github.
After you have the files you need, remove the shim config for jquery-ui since that should be used only for non-AMD scripts.
knockout-sortable is also an AMD module so you don't need any shim config.
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.
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.
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
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.