I am facing loading of JQuery dependency scripts with Require Js
I am having Jquery Plugins, jQuery Library and 2 module js
assets > js > vendor > jQuery Library,
assets > js > apps.js, pageTop.js, categoryJS.js
My apps.js:
require.config({
'baseUrl':'assets/js',
'paths':{
'jQuery':'vendor/jquery-1.11.2.min',
'viewportSize':'viewportSize-min',
'isoTopFilter':'isotope.pkgd.min',
'categoryJS' :'categoryJS'
},
shim:{
'jQuery': {
'exports':'$'
},
'viewportSize' :{
deps:['jQuery']
},
'isoTopFilter':{
deps:['jQuery']
},
'pageTop':{
deps:['jQuery']
},
'categoryJS':{
deps:['jQuery']
}
}
});//require.config
define(['jQuery','viewportSize','isoTopFilter','pageTop','categoryJS'], function($, pageTop, categoryJS) {
$(function() {
pageTop.alertProduct();
categoryJS.categorySlider();
});
});
and my pageTop.js
define([],function( ){
return {
alertProduct: function(){
alert('I am module2');
return true;
}
}
});
My categoryJs.js
define([],function( ){
return {
categorySlider: function(){
alert('I am module2');
return true;
}
}
});
The script is not loading, getting error
Uncaught TypeError: Cannot read property 'alertProduct' of undefined
Can some one please help me, I have tried by all means
You have:
define(['jQuery','viewportSize','isoTopFilter','pageTop','categoryJS'],
function($, pageTop, categoryJS)
Look at the list of dependencies and the list of arguments. pageTop is the 2nd argument so it will get the value of the viewportSize module. There's no magic here: each module listed in the dependencies is passed to the callback and it is passed in the same order as it appears in the dependencies. Assuming you do not need the values of viewportSize and isoTopFilter you could do this:
define(['jquery','pageTop','categoryJS','viewportSize','isoTopFilter'],
function($, pageTop, categoryJS)
Also,
jQuery calls define so you cannot set a shim for it. (It most likely will be ignored.) You should remove the shim you set for it.
Same with your own modules. If you call define and want to set a list of dependencies, pass the dependencies to define, do not set shim for these modules. shim is only for code that does not call define.
jQuery hardcodes its module name as jquery so you have to refer to it as jquery all lowercase. (More details here.) Change your paths so that you have jquery there instead of jQuery.
Related
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.
Is there a way to avoid loading modules that could already exist into the DOM?
Example:
require.config({
paths: {
// jquery here is needed only if window.jQuery is undefined
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min'
}
});
It would be great to be able to use something like this snippet
require.config({
paths: {
'jquery': {
uri: '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min',
// if this function returns false or undefined load the script from the url
define: function(){ return window.jQuery; }
}
}
});
-----------------------------------------------------------------
UPDATE
-----------------------------------------------------------------
I sent a pull request to #jrburke on github https://github.com/jrburke/requirejs/issues/886 with my proposal. The fixed version of requirejs could be tested on here:
http://gianlucaguarini.com/experiments/requirejs/requirejs-test3.html
Here the requirejs configuration according to my API proposal
require.config({
paths: {
// jquery here is needed only if window.jQuery is undefined
'jquery':'//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min',
'lodash':'//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.0.0/lodash.underscore.min',
'backbone':'//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min'
},
shim:{
'jquery':{
// with my fix now I detect whether window.jQuery has been already defined
// in this case I avoid to load the script from the cdn
exports:'jQuery',
// if this feature is missing I need to load the new jQuery from the cdn
validate: function(){
return window.jQuery.Defferred;
}
},
'lodash':{
// lodash will be loaded only if it does not exist in the DOM
exports:'_',
// if this function returns false or undefined load the script from the cdn
validate: function() {
// is the lodash version already available in the DOM new enough for my application?
return window.parseInt(window._.VERSION) >= 2;
}
},
'backbone':{
deps:['lodash','jquery'],
// if backbone exists we don't need to load it twice
exports:'Backbone'
}
}
});
As #jrburke points out in your pull-request, the way to do it is:
require.config({});
if (typeof jQuery === 'function') {
define('jquery', function() { return jQuery; });
}
// start module loading here
require(['app'], function(app) {});
If a module is already defined, it won't be (re)loaded. Here, the definition is simply to re-use the already-loaded global jQuery object.
As jQuery is AMD compatible, if it is already in the page Require.js won't load it again.
In a broader way, Require.js only looks the path configs when a module haven't been defined yet. So as soon you have a module defined, Require.js won't load it again:
define('jquery', [], function() { /* stuff */ });
// ^ Module 'jquery' is defined here. Require.js won't load it twice.
Checkout this JsBin for a working example: http://jsbin.com/OfIBAxA/2/edit
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.
I'm trying to load jqplot as a requireJS module.
My main.js has a path and shim like this:
require.config({
, paths: {
plot: '../js/plugins/jqplot/jqplot.module'
}
, shim: {
'plot': { deps: ['jquery']}
}
});
Since this module is not needed on most pages, I'm waiting for the pageXYZ to be loaded and then inside a <script></script>, I'm calling:
require(['plot'],
function (plot) {
// do stuff
}
);
And my jqplot.module looks like this:
define(['../js/plugins/jqplot/jquery.jqplot'],
function () {
require([
'../js/plugins/jqplot/plugins/jqplot.barRenderer'
, '../js/plugins/jqplot/plugins/jqplot.logAxisRenderer'
, '../js/plugins/jqplot/plugins/jqplot.categoryAxisRenderer'
, '../js/plugins/jqplot/plugins/jqplot.canvasAxisTickRenderer'
, '../js/plugins/jqplot/plugins/jqplot.canvasTextRenderer'
, '../js/plugins/jqplot/plugins/jqplot.pointLabels'
, '../js/plugins/jqplot/plugins/jqplot.enhancedLegendRenderer'
],
function (){
return $.jqplot;
}
);
}
);
which returns the correct object with all sub-plugins defined and usable.
However, my Do stuff code runs BEFORE jqplot is assigned to $, so I'm still getting undefined errors when my code runs (I assume, because the files are all loaded, so requirejs starts running)
Question:
What can I do halt code execution until the jqplot has been assigned to $?
1) I think this line from your question:
define(['../js/plugins/jqplot/jquery.jqplot'],
should read:
define(['../js/plugins/jqplot/jquery.module'],
2) You do not need a shim, because you have a module definition that loads jqplot. So change your requirejs.config to
require.config({
, paths: {
plot: '../js/plugins/jqplot/jqplot.module'
}
});
3) your jqplot.module is not returning anything right now. Change it to:
define([
'../js/plugins/jqplot/jquery.jqplot'
],
function () {
var plot;
require([
'../js/plugins/jqplot/plugins/jqplot.barRenderer',
'../js/plugins/jqplot/plugins/jqplot.logAxisRenderer',
'../js/plugins/jqplot/plugins/jqplot.categoryAxisRenderer',
'../js/plugins/jqplot/plugins/jqplot.canvasAxisTickRenderer',
'../js/plugins/jqplot/plugins/jqplot.canvasTextRenderer',
'../js/plugins/jqplot/plugins/jqplot.pointLabels',
'../js/plugins/jqplot/plugins/jqplot.enhancedLegendRenderer'
],
function () {
plot = $.jqplot;
});
return plot;
});
This is all untested, but I think these should help
Answering this question and all the repeats, basic above answer is close but no cigar, as require is async, as such require could/will fire before define closure closes and returns, async safe solution below :
Nasty problem, as it's a chain of three dependencies
jquery is required for jqplot which is required for jqplot plugins, I have a simpler solution based on the same lines as the one above
first do your requirejs "main.js" config like so
requirejs.config({
paths: {
"jquery": "path/to/jquery-1.10.2.min",
// WORKAROUND : jQuery plugins + shims
"jqplot.core": "path/to/jquery-jqplot-1.0.8.min",
"jqplot": "jquery-jqplot-module-with-plugins-1.0.8"
},
shim: {
"jqplot.core": {deps: ["jquery"]},
"jqplot": {deps: ["jqplot.core"]}
}
});
create a wrapper file module file called "jquery-jqplot-module-with-plugins-1.0.8.js", containing :
// wraps jquery jqplot plugin in define statement
define([
"jquery",
"path/to/jqplot.highlighter.min",
"path/to/jqplot.cursor.min",
"path/to/jqplot.dateAxisRenderer.min",
"path/to/jqplot.canvasTextRenderer.min",
"path/to/jqplot.canvasAxisLabelRenderer.min",
"path/to/jqplot.enhancedLegendRenderer.min",
"path/to/jqplot.pieRenderer.min",
"path/to/jqplot.donutRenderer.min",
], function($) {
var jqplot;
jqplot = $.jqplot;
return jqplot;
});
Then when ever you need jqplot with those plugins, simply call for "jqplot" which will load "jquery" then "jqplot.core" then all the jqplot modules, then finally return the jqplot object :)
require(["jquery", "jqplot"], function ($, $jqplot) {
console.log("Success..Inside Require JS");
console.log("Plot...", $.jqplot, $jqplot);
});
or
define(["jquery", "jqplot"], function ($, $jqplot) {
console.log("Success..Inside Define JS");
console.log("Plot...", $.jqplot, $jqplot);
});
tada! :)
ps jquery plugins are evil, no suggestion how to fix that situation, just a statement of fact
cheers
Ant