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.
Related
Our project is really huge, singe-page enterprise app, based on RequireJS and Backbone.js, and for some complex UI we use jqWidgets.
In particular, these jqWidgets are what is causing us problems.
There are many app features implemented using older jqWidgets 3.3, and for all new ones we would like to use 3.6, but porting old features to 3.6 is very tricky and will cost us time that we don't have at the moment.
What we would like to do to save this time, is having both 3.3 and 3.6 working alongside without creating any problems, and do the porting part later when we can.
What I have tried so far:
requirejs.config({
paths: {
"jquery": "vendor/jquery",
"jqx": "vendor/jqwidgets/3.3",
"jqx3.6": "vendor/jqwidgets/3.6",
... other libs...
},
shim: {
... other shims ...
// jqWidgets3.3
"jqx/jqxcore": {
deps: ["jquery"]
},
"jqx/<<some_plugin>>": {
deps: ["jqx/jqxcore"],
exports: "$.fn.<<some_plugin>>"
},
// jqWidgets3.6
"jqx3.6/jqxcore": {
deps: ["jquery"]
},
"jqx3.6/<<some_plugin>>": {
deps: ["jqx3.6/jqxcore"],
exports: "$.fn.<<some_plugin>>"
},
}
});
Usage in old feature:
require(["jquery", "jqx/<<some_plugin>>"], function($) {
$('<<some_selector>>').<<some_plugin>>(...options...);
});
Usage in new feature:
require(["jquery", "jqx3.6/<<some_plugin>>"], function($) {
$('<<some_selector>>').<<some_plugin>>(...options...);
});
Since both plugins are applied to same jQuery object referencing them with different names/paths works but creates lots of bugs. Example: if first feature you used in app was loaded using jqWidgets 3.3, then next used feature using 3.6 would probably broke, and vice-versa. It only works if you refresh the page after every feature use -- which is kind of pointless since it is single-page app.
So my question is: is it possible to make both jqWidgets 3.3 and 3.6 work alongside by each one of then depend on their own jQuery object so this conflict does not happen?
// Appendix 1:
I think potential solution lies in comment of this question: RequireJS - jQuery plugins with multiple jQuery versions
I'll take a closer look and post here a solution if I find one.
You can use 'map' feature of requirejs,
Map allows you to map the same dependency to different files based on the module which uses dependency.
so you config your build file like this:
requirejs.config({
paths: {
"jquery": "vendor/jquery",
"jqueryForjqx3.6": "toNoConflictJQueryModule",
"jqx": "vendor/jqwidgets/3.3",
"jqx3.6": "vendor/jqwidgets/3.6",
... other libs...
},
map:{
'*':{
.....
},
'jqx':{
'jquery' : 'jquery'
},
'jqx3.6':{
// map jquery to no conlict jquery
'jquery' : 'jqueryForjqx3.6'
},
'jqueryForjqx3.6':{
'jquery' : 'jquery'
}
},
shim: {
... other shims ...
// jqWidgets3.3
"jqx/jqxcore": {
deps: ["jquery"]
},
"jqx/<<some_plugin>>": {
deps: ["jqx/jqxcore"],
exports: "$.fn.<<some_plugin>>"
},
// jqWidgets3.6
"jqx3.6/jqxcore": {
deps: ["jquery"]
},
"jqx3.6/<<some_plugin>>": {
deps: ["jqx3.6/jqxcore"],
exports: "$.fn.<<some_plugin>>"
},
}
});
This configuration maps jquery dependency to different files based on module which uses it.
The content of no conflict version can be like this:
define(['jquery'], function($){
return $.noConflict();
});
You can use http://requirejs.org/docs/jquery.html#noconflictmap about how to use jquery no-conflict and requirejs
if jqWidgets doesn't support it out of the box, I'd try this:
load jQuery
load jqxWidget 3.3
load jQuery again, but in its noConflict mode, and assign it to $2 for instance (I'm not sure RequireJS can do that, but you could copy the jQuery.js file and rename it to something different to load it a second time)
set the 1st jQuery to $1 (window.$1 = $)
set the 2nd jQuery to $ (window.$ = $2)
load jqxWidget 3.6
Now each jqxWidget should have its own, separate jQuery.
Obviously it's not ideal to have jQuery loaded twice, but that shouldn't be a problem apart from using a bit more memory on your app.
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 creating a web application using Backbone.js and Handlebars.js (with Underscore.js and jQuery as dependencies for Backbone). I'm loading modules for the app with requirejs.
Following the instructions here:
http://requirejs.org/docs/jquery.html#noconflictmap
and here:
http://requirejs.org/docs/api.html#config
So my requirejs config looks like:
map: {
'*' : { 'jquery': 'jquery-private' },
'jquery-private': { 'jquery': 'jquery' }
},
shim: {
'underscore' : {
exports: '_',
init: function() { return this._.noConflict(); }
},
'backbone' : {
deps: ['underscore', 'jquery'],
exports: 'Backbone',
init: function(_, $) {
// need to manually set Backbone.$ since it looks for it on the global object
this.Backbone.$ = $;
return this.Backbone.noConflict();
}
},
'handlebars' : { exports: 'Handlebars' }
}
I'm loading local copies of my dependencies by calling noConflict() on backbone, underscore, and jquery. However, Handlebars does not have a noConflict method; if I attempt to configure the shim for it in the same way as backbone & underscore, I get an error:
Uncaught TypeError: Object #<c> has no method 'noConflict'
No real surprise there, but then I'm concerned about conflicts! Is there a workaround? Can I somehow manually achieve the same goal by writing my own version of the noConflict for Handlebars? What would that look like?
As I understand, purpose of noConflict is to release some namespace (or, in other words, global variable). I think, you could accomplish it in following way:
Add to Handlebars some fake dependency (it may be simple AMD module,
written by yourself)
in Handlebars shim config, use init option in following way:
handlebars : {
deps: ['myFakeModule'],
init: function(myFakeModule) {
myFakeModule.Handlebars = this.Handlebars;
this.Handlebars = undefined;
return myFakeModule.Handlebars;
}
}
This is a theory and a bit hacky, but it might do the trick.
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 moved a project to requirejs and everything works fine except for a detail with a 3rd party library (which is not an AMD module). I would like to know any suggestions on the techniques to follow to resolve these type of issues when using requirejs.
The 3rd party library is kendo-ui and the issue is when trying to change the locale by calling kendo.culture("es-MX"). The function is being called without an error but it does not work as supposed.
The way to use this is kendo is by:
loading kendo :
loading the locale :
calling the function: kendo.culture("es-MX");
I checked and the only global variable that gets exported is named kendo by the kendo script. I cannot see any global variable added by kendo.culture.es-MX.min.js
The setup I did in my main script for requirejs is:
require.config({
paths: {
jquery: 'lib/jquery-1.7.2.min',
signals: 'lib/signals',
hasher: 'lib/hasher',
crossroads: 'lib/crossroads',
kendo: 'lib/kendo.web.min',
kendoCulture: 'lib/cultures/kendo.culture.es-MX.min',
knockout: 'lib/knockout-2.1.0',
knockout_kendo: 'lib/knockout-kendo.min',
underscore: 'lib/underscore-min',
json2: 'lib/json2',
faclptController: 'faclpt/faclptController',
FacturaViewModel: 'faclpt/FacturaViewModel',
ConfigViewModel: 'faclpt/ConfigViewModel',
domReady: 'lib/domReady'
},
shim: {
'kendoCulture': {
deps: ['kendo']
},
'kendo' : {
exports: 'kendo'
}
}
});
require([
'require',
'jquery',
'knockout',
'knockout_kendo',
'underscore',
'json2',
'faclptController',
'FacturaViewModel',
'ConfigViewModel',
'domReady'
], function (
require,
$,
ko,
knockout_kendo,
_,
json2,
faclptController,
FacturaViewModel,
ConfigViewModel,
domReady) {
// Start of Main Function
domReady(function () {
kendo.culture("es-MX");
// knockout Bindings
ko.applyBindings(FacturaViewModel, document.getElementById('Proceso'));
ko.applyBindings(ConfigViewModel, document.getElementById('Configuracion'));
});
});
So what else should I look for?
I would appreciate any techniques or tips on how to debug requirejs