I'm putting together a framework using requireJS with a CDN version of jQuery (as is now the recommended approach) and having some issue when optimizing the code. The output is namespaced and I'm specifying that each module use a private version of jquery as outlined in the documentation:
require.config({
// Add this map config in addition to any baseUrl or
// paths config you may already have in the project.
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' }
}
});
// and the 'jquery-private' module, in the
// jquery-private.js file:
define(['jquery'], function (jq) {
return jq.noConflict( true );
});
The problem I'm seeing after optimization is that "jq" is undefined in the "jquery-private.js" file.
Any ideas? I've tried setting jq = $ but that seems to destroy the global.
Thanks.
Here is what I did to get the jQuery CDN & optimization sample linked from the RequireJS jQuery Instructions page to work with the Mapping Modules to use noConflict section that you pasted in your original question.
1 - Forked the sample
2 - Created file www/js/lib/jquery-private.js with this content
define(['jquery'], function (jq) {
return jq.noConflict( true );
});
3 - Modified www/js/app.js to paste the map section so the require.config now looks like this:
requirejs.config({
"baseUrl": "js/lib",
"paths": {
"app": "../app",
"jquery": "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min"
},
map: {
'*': { 'jquery': 'jquery-private' },
'jquery-private': { 'jquery': 'jquery' }
}
});
4 - Modified www/js/app/main.js to use jqlocal instead of $ (just to prove to myself that it's not the global jQuery:
define(["jquery", "jquery.alpha", "jquery.beta"], function(jqlocal) {
jqlocal(function() {
jqlocal('body').alpha().beta();
});
});
5 - Changed to the tools folder and ran:
node r.js -o build.js
6 - Changed to the www-build folder that was created and ran servedir (doesn't really matter what web server but that's what I use for dev)
7 - Browsed to the local address & port number of the app (in my case http://localhost:8000/app.html) and saw:
Alpha is Go!
Beta is Go!
You can see the end result here
To get this working I changed the way I was using Require (possibly how I should have been doing it all along). This information might prove useful to others, so I thought I'd put it out there.
Previously I was specifying any dependencies in the defined module:
define( [ "dep1", "dep2", "jquery" ], function( var1, var2, jq ) {
This worked fine initially, but failed when optimized. I moved the dependencies to the require function call including this module and it then started to work OK both pre and post optimisation, with jquery being used privately:
require( [ 'jquery', 'dep1', 'dep2' ], function( jq, var1, var2 ) {
formValidator.formValidationInit( jq( el ) );
});
I wouldn't have thought this would have made a difference, but it seemed too.
It is also worth noting that I had to change the jquery-private file as it was still throwing up an issue concerning "jq" not being defined. I am now setting jq equal to the global $ and returning it so it can be used pivately:
define(['jquery'], function () {
var jq = $;
return jq.noConflict( true );
});
Related
I am attempting to get started with RequireJS, and am running into an annoying issue. . .
require.config({
baseUrl: 'app_content/scripts',
paths: {
// the left side is the module ID,
// the right side is the path to
// the jQuery file, relative to baseUrl.
// Also, the path should NOT include
// the '.js' file extension. This example
// is using jQuery 1.9.0 located at
// js/lib/jquery-1.9.0.js, relative to
// the HTML page.
'jQuery': 'lib/jQuery/jQuery-2.0.3'
}
});
This, using uppercase Q, does not work, But if I change it to jquery, it does. Also, this is true in my required areas...
<script type="text/javascript">
require(["jQuery"], function ($) {
console.log($);
});
</script>
This returns undefined, but if I change everything to straight up jquery, it works.
Is this expected behavior, and is there anything I can do about it?
Yes, jQuery defines itself as 'jquery', all lowercase. That's normal.
If you open the source to jQuery you'll find:
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function () { return jQuery; } );
}
So you have to refer to it as "jquery" everywhere in RequireJS calls. The issue here is that the define that jQuery uses is a "named define" which is something we normally do not use when creating modules. The RequireJS optimizer adds these names for us when we run it.
At any rate, when a "named define" is used the module name is set to the name given to define rather than by file names (as is otherwise the case when we don't use a named define).
It is possible to rename "jquery" to "jQuery", like this:
require.config({
baseUrl: "./js",
paths: {
"jquery": "jquery-1.10.2"
}
});
define("jQuery", ["jquery"], function ($) {
return $;
});
require(["jQuery"], function ($) {
console.log($);
console.log($("body")[0]);
});
I'm making use of the version of define that takes a name as the first parameter. Full example here.
I have a common.js that defines the config for RequireJS:
(function(requirejs) {
"use strict";
requirejs.config({
baseUrl: "/js",
paths: {
"jsRoutes": "http://localhost:8080/app/jsroutes"
},
shim: {
"jsRoutes": {
exports: "jsRoutes"
}
}
});
requirejs.onError = function(err) {
console.log(err);
};
})(requirejs);
I then have a main.js file that I try to use the jsRoutes path that I created:
require(["./common", "jsRoutes"], function (common, routes) {
// do something interesting
});
but I do not load the resource at http://localhost:8080/app/jsroutes instead it tries to load http://localhost:8080/js/jsRoutes.js when the main.js is executed. But this resouce doesn't exist and I get a 404.
How do I get the jsRoutes path to work correctly? Also do I need the shim (I'm not 100% sure)?
I can debug into the common.js file, so the paths should be being set, right?
Update 1
I believe that the paths should work as I have them defined shouldn't they?
Excerpt from http://requirejs.org/docs/api.html
There may be times when you do want to reference a script directly and not conform to the "baseUrl + paths" rules for finding it. If a module ID has one of the following characteristics, the ID will not be passed through the "baseUrl + paths" configuration, and just be treated like a regular URL that is relative to the document:
Ends in ".js".
Starts with a "/".
Contains an URL protocol, like "http:" or "https:".
Update 2
I may have misread the docs, I can solve the issue by defining the main.js like so:
require(["./common", "http://localhost:8080/app/jsroutes"], function (common, routes) {
// do something interesting
});
I was rather hoping not to have to pass round this rather unwieldy URL though.
Update 3
Further investigation of the docs revealed the following snippet:
requirejs.config({
enforceDefine: true,
paths: {
jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'
}
});
//Later
require(['jquery'], function ($) {
//Do something with $ here
}, function (err) {
//The errback, error callback
//The error has a list of modules that failed
var failedId = err.requireModules && err.requireModules[0];
if (failedId === 'jquery') {
//undef is function only on the global requirejs object.
//Use it to clear internal knowledge of jQuery. Any modules
//that were dependent on jQuery and in the middle of loading
//will not be loaded yet, they will wait until a valid jQuery
//does load.
requirejs.undef(failedId);
//Set the path to jQuery to local path
requirejs.config({
paths: {
jquery: 'local/jquery'
}
});
//Try again. Note that the above require callback
//with the "Do something with $ here" comment will
//be called if this new attempt to load jQuery succeeds.
require(['jquery'], function () {});
} else {
//Some other error. Maybe show message to the user.
}
});
It would seem here that the jquery path is working with a full URL
I'm fairly certain your path should be relative to your baseUrl. So giving it the domain & port is screwing it up.
EDIT: My standard require js config... it might help?
require.config({
baseUrl : "./",
paths: {
// Bower Components
respond: 'assets/bower_components/respond/dest/respond.min',
// Libraries & Polyfills
polyfillGCS: 'assets/js/lib/polyfill-getComputedStyle',
polyfillRAF: 'assets/js/lib/polyfill-requestAnimationFrame',
polyfillPro: 'assets/js/lib/polyfill-promise',
easing: 'assets/js/lib/easing',
signalsui: 'assets/js/lib/Signals.ui',
signalsjs: 'assets/js/lib/Signals',
domReady: 'assets/js/lib/domReady', // TODO: Still needed?
// Modules
app: 'assets/js/es5/app'
},
shim: {
app: {
deps: ['signalsjs']
},
signalsjs: {
deps: ['easing', 'polyfillGCS', 'polyfillRAF']
},
signalsui: {
deps: ['signalsjs']
}
}
});
// Load the app
require(['app']);
Ok I realised what I was doing wrong. It was simple really.
I had dependencies for ./common and jsRoutes being passed to the same module so jsRoutes was being required before it had been defined by the config.
I moved the dependency from the main.js file to where it was actually needed and things worked as I expected.
I had the same problem but I fixed it by changing my code like your original code:
require(["./common", "jsRoutes"], function (common, routes) {
// do something interesting
});
to this:
require(["./common"], function (common) {
require(["jsRoutes"], function (routes) {
// do something interesting
});
});
I'm guessing that, in the original code, RequireJS attempts to load the "jsRoutes" dependency before the configuration changes made in "common" are applied. Nesting the require calls effectively ensures that the second dependency is loaded only after the first is evaluated.
I'm trying to get a jQuery plugin working properly with RequireJS, when using jQuery in the noconflict/noglobal state to force all modules to indicate whether they require jQuery. However, for non-AMD-friendly plugins, the shim config seems to not be working. Namely, if a jQuery plugin is defined with a wrapper like:
(function($) {
$.extend($.myPlugin, { myPlugin: { version:'0.0.1'} });
})(jQuery);
Then the following RequireJS configuration isn't working:
requirejs.config({
paths: {
jquery: ['//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min', 'jquery-min'],
},
map: {
'*': { 'jquery': 'jquery-noglobal' }, // Force all modules to use the non-global jQuery...
'jquery-noglobal': { 'jquery': 'jquery' } // ...except the wrapper module itself, which needs the real one.
},
shim: {
'sadPlugin': {
deps: ['jquery']
}
}
});
jquery-noglobal.js:
define(['jquery'], function(jq) {
return jq.noConflict( true );
});
The error that fires when the plugin code runs is: "can't call extend on undefined", meaning jQuery was never set at the outer level, so $ is undefined inside the self-executing function. I put breakpoints outside the plugin self-executing function, and inside to verify that.
I'm guessing part of the problem is capitalization; the module was written to expect jQuery (camelCase), while the AMD module name is jquery (lower case). Is there any way in the shim config to specify what the injected requirements' variable names should be?
I've also tried adding a sadPlugin: {'jquery':'jquery'} entry to the map hash, hoping to make shim give that module the global jQuery instead of the non-global one, but still jQuery/$ aren't defined by the time the function gets called.
EDIT: Found one kludge that does answer part of the problem: according to the comment found here, the deps of a shim need to be the full file path of the script to load, and cannot be an alias from the paths configuration.
So, since my CDN-fallback file of jQuery is jquery-min.js, if I do:
shim: {
'sadPlugin': {
deps: ['jquery-min']
}
}
The plugin works! However, since the "real" jQuery is now being used, it pollutes the global namespace, and the $ variable is then available without require()ing it, so defeats the whole purpose of the noglobal wrapper...
Just use
return jQuery.noConflict( true );
instead of
return jq.noConflict( true );
So, as local variable inside requirejs, your plugins can use the variable jQuery for the parameter $
(function($) {$.extend($.myPlugin, { myPlugin: { version:'0.0.1'} });})(jQuery);
The config that work for me is:
-- main.js
-- jquery-private.js
In main.js file:
require.config({
paths: {
},
map: {
'*': { 'jquery': 'jquery-private' },
'jquery-private': { 'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js' }
},
shim: {
myplugins: ['jquery']
}
});
In jquery-private.js file:
define(['jquery'], function () {
jQuery = $.noConflict(true);
return jQuery;
});
When building and running RequireJS with CoffeeScript in what I understand to be the ordinary way, I seem to have a problem with code not being executed in the expected order i.e.
<script src="/_s/lib/require-jquery.js"></script>
<script>
require.config({
paths: {
"main": "/_s/all.min", // <--- the 'optimized' result of `$ r.js build.js`
}
});
require(["main"], function () {
// this executes without any regard to whether 'main' is loaded.
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// also:
// require('cs!csmain') throws an exception because "cs!csmain" has not been
// loaded for context: '_'.
});
One would expect the function passed to require(["main"], ... to be executed after main and all its dependencies are loaded, because that's what the documentation says.
However, that is not the case at all. On my local development system this problem does not exhibit itself, I suppose because it is a race condition of some sort, making it doubly problematic because this is cropping up only AFTER deployment/staging.
I have a straightforward main.js like this:
var _paths;
_paths = {
...
underscore: '../lib/lodash'
};
require.config({
baseUrl: '/_s/mycode/', // main.js lives here
paths: _paths,
shim: {
...
'timeago': ['jquery']
},
waitSeconds: 60
});
require(['cs!csmain']); // has all the dependencies
Along with a build.js called as an argument to r.js along the lines of:
({
baseUrl: 'mycode',
optimize: 'none',
out: 'all.min.js',
stubModules: ['cs', 'coffee-script'],
paths: {
...
underscore: '../lib/lodash'
},
name: 'main',
shim: {
...
}
})
Does anyone have any idea what's going on here? I really enjoy the asynchronous nature of RequireJS combined with the ability to split up my code into sensible modules, but this problem is particularly frustrating because it only exhibits on a staging/production setting.
Any thoughts and suggestions would be duly appreciated.
EDIT: Removed some probably-superfluous arguments to shorten the question.
I believe I have sorted this problem out - main.js should contain a call to define, as I posted in an issue on GitHub.
I'm attempting to load the Highcharts library using a shim in RequireJS. However, when Highcharts loads, it throws an exception because it can't access the jQuery methods it depends on.
The require config looks like so:
require.config({
baseUrl: "js",
shim: {
'libs/highcharts/highcharts.src.js': {
deps: ['jquery'],
exports: function(jQuery)
{
this.HighchartsAdapter = jQuery;
return this.Highcharts;
}
}
}
});
The exception that is thrown is:
Uncaught TypeError: undefined is not a function
and is in regards to this line:
dataLabels: merge(defaultLabelOptions, {
The issue is the merge call, which eventually maps itself back to jQuery (or some other adapter that Highcharts supports; but I'm just using jQuery).
I'm not sure exactly how to make sure Highcharts gets access to jQuery using RequireJS and shim.
Has anyone used RequireJS and Highcharts together before? I guess the issue isn't specific to highcharts, but any library that has other sorts of dependencies.
Thanks in advance for any advice or points to the correct direction!
To add further context, in hopes that someone who is familiar with require.js or shims will be able to help without having to be too intimately familiar with highcharts, here's some source that sets up this merge method in Highcharts
var globalAdapter = win.HighchartsAdapter,
adapter = globalAdapter || {},
// Utility functions. If the HighchartsAdapter is not defined,
// adapter is an empty object
// and all the utility functions will be null. In that case they are
// populated by the
// default adapters below.
// {snipped code}
merge = adapter.merge
// {snipped code}
if (!globalAdapter && win.jQuery) {
var jQ = win.jQuery;
// {snipped code}
merge = function () {
var args = arguments;
return jQ.extend(true, null, args[0], args[1], args[2], args[3]);
};
// {snipped code}
}
The win object is a reference set up to window at the beginning of the script. So, I thought adding window.jQuery = jQuery; to the export method on the shim would result in highcharts picking up the jQuery reference; but it didn't.
Again, any insight, info, advice, or heckles would be appreciated at this point - I'm at a complete loss, and starting to question whether trying to implement and AMD package system in browser javascript is even worth it.
After accepting the answer from pabera below I thought it appropriate to update my question to reflect how his answer helped my solution (though, it's basically his answer).
RequireJS uses "paths" to find libs that aren't "AMD" supported and loads them on your page. the "shim" object allows you to define dependencies for the libraries defined in paths. The dependencies must be loaded before requirejs will try to load the dependent script.
The exports property provides a mechanism to tell requirejs how to determine if the library is loaded. For core libs like jquery, backbone, socketio, etc they all export some window level variable (Backbone, io, jQuery and $, etc). You simply provide that variable name as the exports property, and requirejs will be able to determine when the lib is loaded.
Once your definitions are done, you can use requirejs' define function as expected.
Here's my example require.config object:
require.config({
baseUrl: "/js/",
paths: {
jquery: 'jquery',
socketio: 'http://localhost:8000/socket.io/socket.io', //for loading the socket.io client library
highcharts: 'libs/highcharts/highcharts.src',
underscore: 'libs/underscore',
backbone: 'libs/backbone'
},
shim: {
jquery: {
exports: 'jQuery'
},
socketio: {
exports: 'io'
},
underscore: {
exports: '_'
},
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
},
highcharts: {
deps: ['jquery'],
exports: 'Highcharts'
}
}
});
As pabera mentioned before, this is for Require.JS version 2.0.1.
I hope someone gets some use out of this; I know it road blocked me for a little while; so hopefully we kept you from banging your head into the same spot in the wall that we did, by posting this.
I had the exact same problem and I was struggling around many hours until I saw your entry here. Then I started over from scratch and now it works for me at least.
requirejs.config({
baseUrl:'/js/',
paths:{
jquery:'vendor/jquery',
handlebars: 'vendor/handlebars',
text: 'vendor/require-text',
chaplin:'vendor/chaplin',
underscore:'vendor/underscore',
backbone:'vendor/backbone',
highcharts: 'vendor/highcharts'
},
shim: {
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
underscore: {
exports: '_'
},
highcharts: {
exports: 'Highcharts'
}
},
});
Since I use Chaplin on top of Backbone, I am including some more files in my paths attribute. Highcharts has a similar structure to Backbone so I thought I could load it the same way. It works for me now. As you can see, I am introducing highcharts in the paths attribute already to export it as a shim afterwords.
Maybe this helps, otherwise let's try to contribute on it even more to solve your problem.
Although jQuery can be used as an AMD module it will still export itself to the window anyway so any scripts depending on the global jQuery or $ will still work as long as jQuery has loaded first.
Have you tried setting a path? jQuery is an interesting one because although you're encoruaged not to name your modules by the RequireJS documentation, jQuery actually does.
From the jQuery source
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
define( "jquery", [], function () { return jQuery; } );
}
What that means is you will need to tell RequireJS where to find 'jquery'. So:
require.config({
paths: {
'jquery': 'path/to/jquery'
}
});
If you're interested in why jQuery registers itself this way then there is a pretty large comment in the source which goes into more detail