Using RequireJS to load a module and all its plugins - javascript

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

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.

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

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.

requireJS configuration for Modernizr

I am trying to load the Modernizr feature detects dynamically with requireJS.
As Modernizr has built in AMD support this shouldn't be a problem.
My requireJS configuration contains the paths to the Modernizr source directory and to the feature detects directory:
requirejs.config({
paths: {
"modernizr" : "components/modernizr/src",
"feature-detects": "components/modernizr/feature-detects"
}
});
Lets assume one of my views would require the svg test.
My view definition might look like this
define(["feature-detects/svg"], function() { .. });
Unfortunately the svg.js can't find Modernizr.js because all feature detects and Modernizr source files load Modernizr without specifying any directory: define(['Modernizr'], ...
Which results in a very ugly require.config
requirejs.config({
paths: {
"Modernizr": "components/modernizr/src/Modernizr",
"addTest": "components/modernizr/src/addTest",
"ModernizrProto": "components/modernizr/src/ModernizrProto",
"setClasses": "components/modernizr/src/setClasses",
"hasOwnProp": "components/modernizr/src/hasOwnProp",
"tests": "components/modernizr/src/tests",
"is": "components/modernizr/src/is",
"docElement": "components/modernizr/src/docElement",
"feature-detects": "components/modernizr/feature-detects"
}
});
Is there a cleaner way to tell requireJS to search in components/modernizr/src/ whenever it couldn't find the file?
Update
I created an example github project which includes the basic setup and a running demonstration.
Modernizr's AMD structure is (currently) just for its own internal build process. We've discussed exposing this so that it can be used as you've tried, but haven't agreed on a convenient way to do this yet. A Modernizr plugin for RequireJS could be one option.
Are you using Bower? If so, it's worth noting Modernizr isn't suitable for use with Bower yet.
The recommended way to tie Modernizr into your build process at the moment is using grunt-modernizr, which will automatically find references to Modernizr detects in your code (or you can explicitly define them), then you can just use the resulting Modernizr build like any other AMD dependency whenever you need it:
define(['path/to/built/modernizr.js'], function (Modernizr) {
if (Modernizr.svg) {
...
}
});
My suggestion would be to have a shim
Config
paths: {
'Modernizr': 'PATH TO MODERNIZR'
},
shim: {
'Modernizr': {
exports: 'Modernizr'
}
}
=================
You can use define in your script
define(['Modernizr'],function(Modernizr) {
'use strict';
console.log(Modernizr)
// This should log the Modernizr function
//For Example
if(Modernizr.boxshadow){
// Do something here
}
});
If I understand your question correctly, wouldn't you just define your feature like so:
define([
"modernizr",
"feature-detects/svg"
], function(Modernizr) {
Modernizr.addTest();
});
This way modernizr will be loaded before your feature detection code runs.

Always require certain dependencies in RequireJS

I'm working on a Backbone project and I'm loading jQuery, Underscore and Backbone with RequireJS.
I find myself typing this pattern over and over again in all the modules:
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) { ...
Is there a way or workaround to make these 3 libraries available to all modules without explicitly requiring them so I can concentrate on requiring extra things?
I though about loading this dependencies stack within script tags and use RequireJS for my modules and extra dependencies, but I'd lose the JamJS compile feature by having to concatenate jquery, underscore and backbone myself.
EDIT:
See Backbone Boilerplate: They are using JamJS too but they don't require backbone,underscore,jquery on each file. Somehow it's available to all of them.
They require the config.js file within the markup with RequireJS. This exports require.config stuff and then delegates to main.js. Within main, then they have magically access to Backbone!
What happened in the middle?
I have a sandbox as mentioned in the comments. Here is a sample in coffee-script:
define [
"core"
"jquery"
"extensions/backbone"
"underscore"
], (core, $, backbone, underscore) ->
util:
underscore: underscore
mvc:
Model: backbone.Model
Collection: backbone.Collection
View: backbone.View
Events: backbone.Events
Router: backbone.Router
This allows me to do
define ["sandbox"], (sandbox) -> class View extends sandbox.mvc.View
This is similar to the sandbox used in the AuraJS project and an implementation of the facade pattern which has the benefit that...
It lets you expose the parts of a JavaScript library that are safe to
use instead of exposing the entire API. This is particularly useful
when working in teams.
Also, in the future if I want to extend all views from a BaseView class instead of directly from Backbone.View, I just have to change the reference in my sandbox.
Hello you can make globally accessible using the SHIM Config
Example taken from there:
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: ['underscore', 'jquery'],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: '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