require(['underscore'], function ($, _) {
...
});
Doesnt work! (_ is not a function)
How to manage it?
Note that underscore.js doesn't register itself as an AMD module (though it did for a brief time in earlier versions), thus it can't be used in a require() call without some configuration using "shim:" like so:
require.config({
paths: {
jquery: 'lib/jquery.min',
underscore: 'lib/underscore-min'
}
shim: {
"underscore": {
exports: "_"
}
}
});
See the docs at: http://requirejs.org/docs/api.html#config-shim
Before shim: was added to require.js, you could do something similar with the plugin use.js (in case you need to use an older version of require.js).
As of this writing, the current version of require.js is 2.1.8.
Alternatively, you can use lodash.js as a drop-in replacement for underscore.js - it does register itself as an AMD module, so you can use it with no extra config: http://lodash.com/
I think the problem is the order of args passed in to your callback.
Should be:
require(['underscore'], function (_, $) {
...
});
Also you need to be using underscore version 1.2.1 which added this functionality.
require(["underscore"], function() {
console.log(_ === window._);
});
it all depends where the script is based.
since i don't see you specified a baseUrl, the baseUrl will be the default,
that means, either 2 things:
your script is directly inside a html file, and in your case it
will thus look for underscore.js in the same directory of the html
file
your script is in a javascript file referenced by your html
file, it will now search for underscore.js in the directory of your
custom javascript file.
check if the underscore.js is actually there.
Here are the checkpoints for you to make sure what you need works
Get require-jquery.js and put it to your /js-root dir
Add to your HTML, right before the closing </body> tag: <script data-main="/js-root/main-js-file-name" src="/js-root/require-jquery.js"></script>
Get underscore adapted for AMD, and put it to /js-root dir as well
In main-js-file-name.js
write:
require(["jquery", "underscore"], function ($, _) {
...
});
Similarly, in your non-main AMD JS files, when defining a module, to use _, write:
define(["jquery", "underscore"], function ($, _) {
...
return theModuleObjectOrFunction;
});
Related
Maybe I have fundamentally misunderstood how requirejs config works but I thought my configuration below made some libraries global so I could just use them in other files while only having to require and define files that I needed to use within the individual script. However I cannot reference $ (jQuery) in my application code without getting a reference error indicating it is not globally accessible. I've isolated the problem to the simple example below.
My file set up is as follows:
test
|
|-index.html
|-TestApp.js
|-MainApp.js
|-lib
| |-require.js
| |-jquery.js
| |-loadash.js
| |-backbone.js
|-css
|-test.css
The library file versions are RequireJS 2.1.22, jQuery 2.0.3, Loadash 3.10.1 and Backbone 1.2.1. I'm just trying to set up my environment and the approach I am taking is to pass my TestApp.js file to require.js to load the required files and bootstrap the application code in MainApp.js. The script in index.html is as follows:
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' type='text/css' href='css/test.css'/>
</head>
<body>
<div></div>
<script src="./lib/require.js" type="text/javascript" data-main="./TestApp.js"></script>
</body>
</html>
The referenced css script file simply ensured the div is visible as an orange square. See below:
div {
height: 100px;
width: 100px;
background-color: #FA6900;
border-radius: 5px;
}
It's the script line in index.html that then kicks off the application code by passing my configuration file to requirejs. This is the TestApp.js passed across as data-main. The TestApp.js is here:
require.config({
paths: {
'jquery': 'lib/jquery',
'lodash': 'lib/lodash',
'backbone': 'lib/backbone'
},
map: {
'*': {
// Backbone requires underscore. This forces requireJS to load lodash instead:
'underscore': 'lodash'
}
},
shim: {
jquery: {exports: '$'},
underscore: {
deps: ['jquery'],
exports: '_'
},
backbone: {
deps: ['underscore'],
exports: 'Backbone'
},
TestApp: {
deps: ['backbone'],
exports: 'TestApp'
}
}
});
require(['MainApp'], function(MainApp) {
MainApp.run();
});
The file above references the paths to the library files I want to use, I then remap loadash to be loaded when underscore is required (I need some of the extra loadash capability), I then use the shim to ensure the dependancies are correct as the files are loaded. Passing this config file to require.js in the index.html seems to be working as all of the files are showing as loaded in my browser. However the problem seems to be they do not appear to be globally accessible as I thought they would be.
Following the config section the last require call loads the MainApp.js file and calls the exposed run function. The MainApp.js looks like this:
define(function(require) {
var run = function() {
$(document).ready(function() {
$('div').click(function() {
$('div').fadeOut('slow');
});
});
};
return {
run: run
};
});
As far as I understood I should not need to require the files I already mentioned in the require config, I thought they should be loaded and available to this code. This is where I have misunderstood what is going on or have missed a step out. The exposed run function is being called but the first line that calls $ throws the error:
ReferenceError: Can't find variable: $
So my questions are:
What have I got wrong in my thinking?
(or) What am I doing incorrectly?
What should I be doing in order to preload and make available
frequently referenced libraries so that I do not need to require and
define them in every file I have?
As far as I understood I should not need to require the files I already mentioned in the require config, I thought they should be loaded and available to this code.
You misunderstood how RequireJS works. You should read the documentation from start to finish. For now, here are things you should change.
You should require jquery in your MainApp module:
define(function(require) {
var $ = require("jquery");
You should remove your shims that you have for jquery, underscore and backbone as they all call define and shim is only for code that does not call define. I don't know what TestApp is but if it is your own code, you really should make it into a proper AMD module and remove the shim.
#Louis has made me realise the error in what I was doing above. Changing the shim in TestApp.js so that is reads:
MainApp: {
deps: ['backbone'],
exports: 'MainApp'
}
Corrected the problem, now Backbone, $ and _ are all available to the rest of my application code without cluttering up each files require. i.e. I do not need to begin every file with:
define (['lib/jquery', 'lib/loadash', 'lib/backbone'], function($, _ , Backbone) {
Given in my actual app the list of common deps is quite large this means I only need to define locally used resources and can control the paths from a single location.
I'm trying to load a library that compiles to Webpack in a require.js project. While the library exposes an object, it returns null when required from the require.js project :
define(function(require, exports, module) {
[...]
require("./ext/mylib.core.js"); // -> null
})
Is there any flags that I can use in Webpack to enable AMD compliance ? There are some references to AMD in the generated library but as it is it does not seem to do anything.
The solution was in Webpack documentation : there is an outputLibrary flag that can be set to "amd" or "umd" and in that case webpack produces amd compliant modules.
EDIT 3:/EDIT: 4
Webpack is not cooperating it may seem, so another possibility would be to expose the module with the shim config option:
require.config({
paths: {
// Tell require where to find the webpack thingy
yourModule: 'path/to/the/webpack/asset'
},
shim: {
// This lets require ignore that there is no define
// call but will instead use the specified global
// as the module export
yourModule: {
exports: 'theGlobalThatIsPutInPlaceByWebpack'
}
}
});
This obviously only works in the case that the webpack stuff is putting something in the global scope. Hope this helps!
EDIT 2:
So I got the question wrong as pointed out in the comments. I didn't find any built-in functionality to produce AMD modules from webpack - the end result seems to be a static asset js file. You could wrap the result in a
define(function () {
return /* the object that webpack produces */;
});
block, maybe with the help of some after-build event (e.g. using this after build plugin for webpack). Then you should be able to require the module with an AMD loader.
Original Answer:
require.js loads it's dependencies asynchronously, you have to declare them explicitly when you're not using the r.js optimizer or the like. So if the module exposes an AMD definition it should work like this:
// It works the way you did it ...
define(['path/to/your/module'], function (require, exports, module) {
require('path/to/your/module'); // -> { ... }
});
// ... but I personally prefer this explicit syntax + it is
// friendlier to a code minifier
define(['path/to/your/module'], function (yourModule) {
console.log(yourModule); // { ... }
});
Maybe you have to configure your require instance, there are docs for that.
EDIT1: as pointed out the way the module is being accessed is not wrong but the dependencies were missing, so I added code that is closer to the original question.
I'm working on a JavaScript module that uses jQuery, some functions of jQuery UI (draggable) and jPlayer. Recently I made myself familiar with requireJS to manage the dependencies properly.
I don't want to produce conflicts with a possibly already existing jQuery version that the site that includes my script uses. For this reason I am mapping the jQuery dependencies to a module "jquery-private" with a call of noConflict(), as is described in the requireJS guide.
As jQuery UI takes up a lot of space, I would also like to just include the modules that I am actually using. ui.draggable has the dependencies ui.core, ui.mouse and ui.widget, so I should have to include these 4 modules.
My problem is that I would like the jQuery UI modules and the jPlayer module to use my own version of jQuery, but obviously it isn't accessible by the global $ variable after I called the noConflict() method. Unfortunately neither jQuery UI nor jPlayer are AMD modules, so I needed to make shim configurations for them.
Here is my definition of the dependencies:
require.config({
baseUrl: 'javascript/modules',
paths: {
jquery: 'jquery-2.1.3',
jPlayer: 'jquery.jplayer',
uiCore: 'jquery.ui.core',
uiMouse: 'jquery.ui.mouse',
uiWidget: 'jquery.ui.widget',
uiDraggable: 'jquery.ui.draggable'
},
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: {
jPlayer: {
deps: ['jquery']
},
uiCore: {
deps: ['jquery']
},
uiMouse: {
deps: ['jquery','uiCore']
},
uiWidget: {
deps: ['jquery','uiCore']
},
uiDraggable: {
deps: ['jquery','uiCore','uiMouse','uiWidget']
}
}
});
require(["json","jquery","jPlayer","uiDraggable"], function(json,___jQuery,jplayer,uiDraggable) {
(...)
}
Obviously this code produces errors as the $ variable in the jQuery UI modules is not defined.
Is there any way to pass my own jQuery object to modules? The top answer in another thread (How use require.js to load jQuery with noConflict) suggests that what I am trying to do is not possible, but maybe there is some other way to do it?
If there is none, I probably have to use global variables and heavily edit the included modules, which kind of makes it questionnable why to use a dependency management library like requireJS in the first place...
I found the following code on top of each module in jquery.ui:
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "jquery" ], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {...});
And it means jquery.ui checks when global AMD "define" function is defined and uses 'jquery' as AMD reference for module.
It will use no conflict of jquery based on requirejs recommendation in this and this.
And about how to use jQuery with AMD.
The line $("body").slimScroll(); throws error, because slimScroll is not defined. That is because require.js probably doesn't load automatically shim keys, if you are requiring any module, that is specified as dependency in shim (i think if i have shim jquery.slimscroll and its dependency is jquery, then when i require jquery, it automatically loads jquery.slimscroll - this behavior doesn't happen).
So the right way is call define(['jquery', 'jquery.slimscroll') function ($) ..., so require knows i need jquery.slimscroll and because it is in shim, it will be loaded a bit different, because it is not AMD compatible.
Problem is that if i have lot of jquery plugins, i need always pass all plugins that i need in define call. Is there any way, how to solve this type of annoying behavior and write just define(['jquery'], function ($) ..?
require.config({
baseUrl: 'js/bower',
paths: {
react: 'react/react-with-addons',
jquery: 'jquery/dist/jquery',
'jquery-private': '../jquery-private',
'jquery.slimscroll': 'jquery-slimscroll/jquery.slimscroll.min'
},
map: {
'*': {
jquery: 'jquery-private',
},
'jquery-private': {
jquery: 'jquery'
}
},
shim: {
'jquery.slimscroll': ['jquery']
}
});
require(['jquery'], function ($) {
$("body").slimScroll();
});
You could just add the jquery plugins to the dependencies of your jquery-private module.
define(['jquery', 'jquery.slimscroll', ...], function (jQuery) {
return jQuery.noConflict(true);
});
This way, when any module requires jquery, the plugins are also loaded at the same time.
I'm assuming that jquery-private is meant to load jQuery so that it does not conflict with other version of jQuery. That's what my example reflects above but it does not generally matter what the module actually does.
With this method you still need to have a shim configuration for each plugin that is not an AMD module. Moreover, your plugins will have to get a map setting just like for jquery-private to get the real jquery module. Otherwise, there will be a circular dependency.
I'm upgrading from jQuery 1.8 to 1.9 and since jQuery.browser() is removed, I will use jQuery Browser Plugin.
My requirejs config file (loaded using data-main="") looks somewhat like this:
(EDITED - added more code snippets)
main-comp.js
require.config({
paths: {
jquery: 'libs/jquery/jquery1.9.1.min',
utils: 'modules/utils',
myController: "controllers/myController",
browserPlugin: 'libs/jquery/jquery.browser.min'
},
shim: {
browserPlugin: {
deps: ['jquery']
}
}
});
require(['myController', 'jquery'], function (controller, $) {
$(controller.start);
}
);
moduls/utils.js
define(['browserPlugin'], function () {
return {
browser: $.browser
};
});
myController.js
define(['utils'], function (utils) {
function start() {
console.log(utils.browser.msie)
}
return {
start: start
};
});
Everything seemed to work properly, but then I saw that sometimes in IE only I get a 'jQuery' is undefined (it's a capital Q there) or '$' is undefined errors from the jquery.browser.min.js file.
I thought the deps means that jquery will load before the jquery.browser file but apparently this isn't always the case. I tried following this answer and add exports: "$.fn.browser" but with no success.
When running an optimized version (minify+uglify using r.js) I haven't encountered it.
What am I doing wrong?
You need to ensure you reference $ as a parameter in the require callback. Like so:
require(['myController', 'jquery'], function (controller, $) {
$(controller.start);
}
);
This ensures that jQuery is available to use. It is a bit of an odd one as it will expose itself globally anyway so it will sometimes work regardless, but the correct way is to explicitly require it and use it inside the callback as a parameter.
It looks like you are missing jquery dependency in moduls/utils.js, please try:
define(['jquery', 'browserPlugin'], function ($) {
return {
browser: $.browser
};
});
and also, just to be on the safe side, add jquery to your shim :
jquery: {
exports: "$"
},
By the way, why don't you use $.browser in your code and just load the jquery plugin using the shim configuration?
I had the same problem, the script in data-main is loading asynchronously, that means that it may load after the scripts it defines.
The solution is to load another script with the require.config right after the require.js script.
data-main Entry Point Documentation.