I’ve been given a design library from a large cooperate client built utilizing Bootstrap 3 and Require.js. It’s very robust and well-built with the various scripts using Require.js to load jQuery. Problem is, Drupal has already loaded jQuery and the double load is causing some items to fail. If I remove all the Require.js calls, Drupal jQuery stuff works great. If I remove the base Drupal jQuery file, all the stuff using the Require.js stuff works great. Here’ are options I have considered:
Stop jQuery from loading with Drupal, use require to load jQuery on every page - I haven’t tried this yet, but it just seems wrong. I think I can remove jQuery with hook_library_alter() in my theme? Wondering if anyone has tried this… My front end and admin screens actually use 2 different themes, so I think I'd have to modify both.
Stop jQuery from loading with Require.js – I tried this and none of the other libraries that are loaded with require.js can find query if it’s not loaded with Require.js, so I don’t think this option works. This is my first time using require.js so maybe there is some fun fix for this I haven't figured out?
Add all libraries to the site with Drupal – This seems like a lot of messy work as I would have to remove all the require.js dependencies in the library, and there is an expectation that we not modify the library/framework so implementing new releases is easy.
Any other recommendations?
UPDATE per Nikos Paraskevopoulos’ answer:
I have configured Drupal to load the jQuery version that came with the require.js package, and Drupal is happy with that. So now on each page I am loading in the following order:
/components/jquery/jquery.js
/misc/jquery.once.js
/misc/drupal.js
/components/requirejs/require.js
/js/require.config.js
My requires.config looks like this:
requirejs.config({
baseUrl: "./",
paths: {
.....
jquery:
"components/jquery/jquery", "jquery-csv": "components/jquery-csv/src/jquery.csv",
"jqueryui-sortable-amd": "components/jqueryui-sortable-amd/js/jquery-ui-1.10.2.custom",
.....
},
shim: {
"jquery-backstretch": [
"jquery"
],
masonry: [
"jquery"
],
OpenLayers: {
exports: "OpenLayers"
},
"jquery-csv": [
"jquery"
]
}
});
If I remove the jquery entry from paths, and add:
jquery: {
exports: 'jQuery' // NOTE THE CASE OF 'Q', THIS IS THE GLOBAL NAME
}
I immediately get errors like:
Uncaught Error: Script error for: jqueryui-sortable-amd
Which makes sense as they have all be declared together. uggg.
Regarding option 2:
I do not know how exactly things load, but I guess Drupal loads jQuery + other stuff it needs, then RequireJS is loaded, which in turn loads its own version of jQuery + other stuff it needs.
First of all you should ensure that the both Drupal and your RequireJS app can play with the same version of jQuery. Then in your RequireJS configuration shim jQuery, instead of loading it normally, e.g. instead of:
require.config({
...
paths: {
// REMOVE THIS IF USED (PROBABLY IS)
jquery: 'lib/jquery-x.y.z'
...
Do:
require.config({
...
shim: {
jquery: {
exports: 'jQuery' // NOTE THE CASE OF 'Q', THIS IS THE GLOBAL NAME
}
...
Explanation: If jQuery is loaded after RequireJS, it defines itself as jquery (lower-case Q). This is the name used by other modules to depend on jQuery. On the other hand, if it is loaded before RequireJS, it does not define itdelf and you are left with the jQuery (and possibly $) globals. The shim above instructs RequireJS to use the global jQuery variable whenever a module depends on the jquery module, which hopefully achieves what you want, i.e. loading jQuery only through Drupal.
Related
Am new in using require JS Framework . Currently am writing angular directives that can be used in a web app(html) as reusable web components. In angular modules& directives I have defined the dependency with require JS. In the html page if few dependencies say angular JS, Jquery are already loaded does it get recognized by the require JS framework in directives.
The directives can come from different JS, is it possible to load even require JS only once.
Any suggestion to achieve loading JS files only once is appreciated
Thanks
Santhosh
RequireJS does not know about anything else than what it itself loads. If a script element loads jQuery, and then a RequireJS module requires the module jquery, then RequireJS will try to load jQuery again. It won't test for the existence of a jQuery loaded with a script element before RequireJS was loaded.
This being said, you can write code to handle such scenario. For instance, the configuration you give to RequireJS could check whether jQuery is already defined:
if (typeof jQuery === 'undefined') {
require.config({
paths: {
jquery: ...path/to/a/cdn/or/a/local/copy...
}
});
}
else {
define('jquery', [], function () { return jQuery; });
}
The true branch of the if is for when jQuery has not been loaded. It configures RequireJS to find it. The false branch defines a jquery module that merely returns the jQuery global so that RequireJS does not actually load jQuery anew.
You should be able to perform a similar test for Angular. Remember that require.config can be called multiple times and RequireJS will combine the values passed in multiple calls.
Good morrow!
I've spent many days searching for a similar question and answer, but can't find (or maybe understand) any similar ones.
The Summary
I am adding onto a series of pages for a client that currently use jQuery 1.4.2. I am limited in that existing code must stay as is, and cannot be leveraged that much.
Yadda, yadda, yadda. All I've built thus far uses RequireJS 2.1.9, jQuery 1.10.2, and my own modules. I've made a jQuery loader module that calls $.noConflict(true) and all that good stuff to ensure my modules get 1.10.2 and I don't intrude on the globally necessary 1.4.2.
Below is an similar example of my current RequireJS configuration:
{
baseUrl: '/some/path',
paths: {
'jquery': 'lib/path/jquery',
'jquery-loader': 'lib/path/jquery-loader',
'jquery-validator': 'lib/path/jquery-validator'
},
map: {
'*': {
'jquery': 'jquery-loader'
},
'jquery-loader': {
'jquery': 'jquery'
}
},
shim: {
'jquery-validator': ['jquery']
}
}
The Problem
Some coworkers need to use some plugins inside my new code, namely jquery-validator.
My problem is that with the current configuration above, jquery-validator, which is a non-AMD plugin gets set to the globally available jQuery (1.4.2) instead of my jQuery (1.10.2) despite the entire setup.
I'd rather all the new code not have to use the globally available jQuery just to get to the validator plugin.
The Question
I'm not sure how I can get this to go as I want it, but my question is: is there actually any way to get this to work without me having to go into jquery-validator and wrap it in a define call?
I'm an experienced javascript programmer, and I'm trying to write my own modular game engine from scratch. I haven't used requirejs before, but after reading a bit about it, it sounds like it's probably the best way to manage all the components of the engine and load them into one coherent structure.
My main problem is that I'm not really sure how to really use requirejs. I've been looking over their API documentation, but I'm not sure how to integrate it with the way I've laid out my project.
Currently, my project uses the following structure:
src
engine.js //This contains the common engine stuff, most notably the engine namespace
resource
resource-module.js //This is the module constructor, which handles all the common stuff between the different
substructures
sprites.js //This is a substructure that contains sprite loading code
render
etc...
third-party
jquery
requirejs
I want to be able to load the modules independently of each other. It should be possible for instance to remove the audio module from the engine, and it still work. It should also be easy to substitute modules, or add new modules.
Also, I'm using jQuery for event handling, so it needs to be loaded before the engine is initiated.
You can find my current source here: https://github.com/superlinkx/lithium-engine
I know the current code is messy, and there isn't a whole lot built yet, but I'm mostly still figuring out how to best structure it. Any help/advice would be appreciated, but my main concern is how to structure it with requirejs so that it will be easier to compile into a single file for production use.
Require.js does not enforce any specific structure of your files. You can either specify the full paths in the require configuration, or just use the full path in the require() or define() calls. Both will work, however the latter will save you some typing and makes it easier to move stuff around when you include something from a lot of different places:
var $ = require("third-party/jquery");
require.config({
paths: {
"jquery": "third-party/jquery"
}
});
var $ = require("jquery");
I want to be able to load the modules independently of each other. It should be possible for instance to remove the audio module from the engine, and it still work. It should also be easy to substitute modules, or add new modules.
This is not something require.js does four you. You can decide when and when not to load it, but you would have to make sure that it won't break the rest of your code.
Also, I'm using jQuery for event handling, so it needs to be loaded before the engine is initiated.
You can do this in several different ways.
require() it in your main.js so that it is always loaded (you can also use the require-jquery.js, which has jQuery included).
Specify jQuery as a dependency of your engine
require.config({
paths: {
"jquery": "path.to.jquery",
"engine": "path.to.engine"
},
shim: {
"engine": {
"deps": ["jquery"]
}
}
});
Pass jQuery to the define() call in your module (probably the best choice)
define(["jquery"], function ($) {
// your engine code
}
I am integrating breezeJS into an existing requireJS project which already uses knockoutJS. I ran into a couple of problems.
The first was that breeze is unable to load the Q library unless i include it on my html wrapper as a <script> tag, not as a loaded AMD dependency. In my project i am trying to keep my code down to a single script tag, so this isnt ideal.
The second issue is that breezeJS is unable to load knockout. In my main.js I have defined a path for knockout to be:
knockout: '../libs/knockout/knockout-2.2.0',
(I do this because I like knowing for sure that I am not accessing a global ko)
However when i added breeze to my project, breeze wasn't able to load my knockout library. Looking into the breeze code i can see that it has been hardcoded to load the knockout library as ko.
Not wanting to change all of my code i found that i could add my AMD loaded knockout library to the global window object as window['ko']. But this feels like quite a bodge. Also weirdly adding Q this way and removing the <script> tag didn't work, as i think Q is required too early in the application's lifecycle, even before i get to pollute the global - i did nest my require() calls in main.js but that hid the majority of my application files from the build process so i abandoned that approach.
How can i include Q and knockout and breeze in my project and still use a single line <script> tag, at the moment I am having to include Q as a separate <script> tag and pollute the global to get breeze and knockout to play nicely.
I am using quite a few other libraries in my project and none of them have been this difficult to integrate in.
Any help is much appreciated
Cheers
Gav
EDIT: Here is my full require config:
require.config({
/**
* shims are for 3rd party libraries that have not been written in AMD format.
* shims define AMD modules definitions that get created at runtime.
*/
shim: {
'jqueryUI': { deps: ['jquery'] },
'jqueryAnimateEnhanced': { deps: ['jqueryUI'] },
'jqueryScrollTo': { deps: ['jquery'] },
'touchPunch': { deps: ['jquery'] },
//'Q': { exports: 'Q' },
//'breeze': { deps: ['Q', 'knockout'], exports: 'breeze' },
'path': { exports: 'Path' },
//'signalR': { deps: ['jquery'] },
},
paths: {
//jquery
jquery: '../libs/jquery/jquery-1.7.2.min',
'jquery.adapter': '../libs/jquery/jquery.adapter',
//jquery plugins
horizontalNav: '../libs/jquery/plugins/horizontalNav/jquery.horizontalNav',
jqueryUI: '../libs/jquery/plugins/jquery-ui/jquery-ui-1.9.2.custom',
jqueryAnimateEnhanced: '../libs/jquery/plugins/animate-enhanced/jquery.animate-enhanced',
touchPunch: '../libs/jquery/plugins/touch-punch/jquery.ui.touch-punch.min',
//jqueryScrollTo: '../libs/jquery/plugins/jquery-scrollTo/jquery.scrollTo.min',
//reveal: '../libs/jquery/plugins/reveal/jquery.reveal',
//opentip: '../libs/jquery/plugins/opentip/opentip-jquery',
//RequireJS
domReady: '../libs/require/plugins/domReady',
text: '../libs/require/plugins/text',
async: '../libs/require/plugins/async',
depend: '../libs/require/plugins/depend',
json: '../libs/require/plugins/json',
noext: '../libs/require/plugins/noext',
//coffee-script
'coffee-script': '../libs/coffee/coffee-script',
cs: '../libs/require/plugins/cs',
//Path
path: '../libs/path/path.min',
//Knockout
knockout: '../libs/knockout/knockout-2.2.0',
knockoutTemplateSource: '../libs/knockout/ko.templateSource',
knockoutValidation: '../libs/knockout/ko.validation',
//breeze
Q: '../libs/breeze/q',
breeze: '../libs/breeze/breeze.debug',
//Signals (Observer pattern)
signals: '../libs/signals/signals',
//SignalR - Push notifications
signalR: '../libs/signalR/jquery.signalR-0.5.2.min',
//utils
logger: 'common/logging/logger',
tinycolor: '../libs/tinycolor/tinycolor',
composing: 'common/composition/composing',
//app specific
BaseWidgetViewModel: 'app/view/core/BaseWidgetViewModel',
}
});
Sorry for the (holiday) delay in addressing your question.
I understand and appreciate your goal of eliminating every script tag except the one for RequireJS. This goal is not easily obtained in my experience.
You did uncover a Breeze defect. Breeze internally is referencing a different 'require' function than your application's 'require' function. It doesn't know about the application 'require' function or its configuration. Therefore, when you omit the Q script tag, Breeze cannot find Q ... no matter how well you configure the application 'require'.
We'll have to fix that. I'll add a comment here when we do.
Meanwhile you'll must use a script tag for 'Q' and for 'KO' and you must put those tags above the script tag for RequireJs. Please continue to use require for your application scripts.
Unfortunately, you have other problems that are unrelated to the dual-require-function problem.
First, I think you will always have trouble keeping KO out of the global namespace ... and this has nothing to do with Breeze.
In KO's AMD implementation (at least when I last looked), KO is either in the global namespace or in the require container; never both. Unfortunately, many useful plug-ins (bindingHandlers, debug.helpers) assume it is in the global namespace; you won't be able to use them if you load KO with require.
You can load Knockout in script tags before Require and then shim KO into the Require container during configuration. Your configuration might look like this:
define('knockout', [], function () { return window.ko; });
The jQuery developers realized this would be a problem. Too many good plugins assume jQuery is in the global namespace. Rather than be purists, the jQuery maintainers (correctly in my view) put jQuery in both the Require container and in the global namespace.
Second, many other useful libraries do not support Require. Your code can be written as if these scripts were designed for require. I still think the easiest, clearest way to handle this is specify them in script tags. Then you can add them into the container by defining them with define calls as shown above. With this shimming, your own modules can be consistent in relying on Require for service location and dependency injection of these libraries.
Third, I think you have a bug in your configuration. For reasons that escape me, you decided to refer to the Knockout module as "knockout" in your Require universe.
Fine ... but you shouldn't expect Breeze to anticipate that module name when we fix it to use the application's require container. Breeze will be looking for KnockoutJs under its standard name, "ko". Breeze will not find it under your preferred name, "knockout".
This is not a big deal. If you're wedded to the "knockout" name, you can make the same instance available under both names. I'm pretty sure the following shim line will work when added to your configuration code:
define('ko', ['knockout'], function (ko) { return ko; });
Again ... this will be effective only after we fix our Breeze require bug.
John Papa had to address many similar issues in Code Camper; you might look at what he did there, especially at main.js.
This should be fixed as of Breeze v 1.2.4. We no longer use an internal implementation of "require".
Sounds like your require config is not right. Could you post the code of your require config?
I am curious:
keep my code down to a single script tag
why that?
Edit: Now i get it. Still sounds like the require config is wrong.
My application's JavaScript uses jQuery and jQuery plugins and running in "hostile" environment I have no control over (this is PHP extension for eCommerce platform). Thus no way to determine whether my jQuery code will be executed before someone will attach his instance of jQuery/plugins (introduced by other extension) or after this or someone will load jQuery dynamically after page rendered.
Basically the problem is that other extension could use jQuery (with plugins) also and just connecting jQuery with tag will not work.
I have a strong feeling that RequireJS might help me to load needed version of jQuery as far as particular versions of jQuery plugins into the encapsulated scope without polluting global scope (so other extensions will still function properly). Then I'll wrap all my code to "require" statements and it will run using it's own set of jQuery and plugins. I tried this and it kind of works (have not tested this in production environment though) but in some weird way. Seems like this question is kind of relevant to problems I have. Also answer suggesting to use AMD-compatible version of jQuery. But what about plugins? I don't think all plugins I use have such versions.
So questions:
Could RequireJS be used to cover such use case (running jQuery+plugins in undefined environment)? If RequireJS could be used there then any example code or explanation of how to do this properly will be greatly appreciated.
If there is no way to cover this with RequireJS what do you think would be best approach to handle issue?
Yes, I believe RequireJS can help you out here. To my knowledge you'll have to do some legwork, though. Take a look at the source of the (as of 2012-08-19) latest jQuery: link. At the bottom you can see that window.jQuery is being set, defining jQuery and $. The define call for AMD loaders happens after that, so jQuery is in the global scope no matter what. What you want to do is guarantee that your own version of jQuery and plugins stay isolated from other jQuery instances and plugins, correct?
Place your custom jQuery and plugins into their own directory (let's call it "jqcustom") and use that directory when specifying dependencies in your scripts' define calls. You'll want to modify your version of jQuery by wrapping it in a define call and returning the jQuery object at the very bottom, taking out jQuery's window.jQuery and window.$ assignments as well as its default define call in the process (these are both at the bottom, remember). Then you'll need to wrap all of your plugins in define calls and specify your own version of jQuery as the dependency. Example:
define(['jqcustom/jquery'], function(jQuery) {
//inside this method 'jQuery' will override any predefined global window.jQuery
//in case any plugins use the $ variable, define that locally too
var $ = jQuery;
//... plugin code ...
});
If you use RequireJS' optimizer, it can do the wrapping of the plugins for you with its shim config options. jQuery plugins work by adding methods to jQuery's prototype object, so I believe as long as you pass the same jQuery (your custom jqcustom/jquery one) to every plugin with your define wrapping, the plugins' extensions will all be set on the same object and be accessible in subsequent define calls specifying your custom jQuery or custom plugins as dependencies. Good luck!