RequireJS: Automatically load configuration script after a library module has been loaded - javascript

I am refactoring the JavaScripts of our project to use RequireJS for on-demand loading of required modules instead of adding a bunch of script tags to the HTML template.
We use a few libraries like jQuery, DataTables plugin for jQuery etc. and some of those libs need some initialization after they have been loaded. I. e. the default settings for DataTables must be set after the lib has been loaded and before it is being used the first time.
At the moment I do this in a main script which is being loaded right after RequireJS. This main module requires all libraries that need initialization, like DataTables, and sets the default settings
require(["jquery", "datatables"], function($) {
// Set datatables default values
$.extend(
$.fn.dataTable.defaults,
{
jQueryUI: true,
lengthMenu: [5, 10, 25, 50],
paginationType: "full_numbers",
pageLength: 5
}
);
});
This approach is quite annoying for two reasons:
I would rather have a single config file for each lib so I don't have to mess around in a potentially huge main script to change settings
The main script always loads every lib to initialize its settings even though some of the libs may not be used on the current page
To improve this, I am looking for some kind of "after-load" dependency or callback, which is automatically loaded or executed when the library has been loaded.
I thought about using the init callback of the shim config for those libraries, but since my libraries don't pollute the global namespace and only the dependencies are being passed to the init function, I have no chance to access the loaded module inside init (as far as I could see).
Then I thought about tinkering with the map configuration of RequireJS to map i. e. DataTables to a wrapper script which requires the actual DataTables lib and sets configuration options afterwards.
Is there a more straightforward way to load the configs?

Just wanted to let you know my final solution. I gave in to using a wrapper script and the map configuration.
The relevant parts of the RequireJs configuration:
// Configure the RequireJS library
var require = {
baseUrl: "js/modules",
paths: {
"jquery": "../lib/jquery/dist/jquery",
"datatables": "../lib/DataTables/media/js/jquery.dataTables",
},
map: {
// Map the 'datatables' library to its wrapper module
// for every other module that 'require's this library
'*': {
'datatables': 'application/datatables.wrapper'
},
// only the wrapper script is supposed to get the actual
// 'datatables' library when 'require'ing 'datatables'
'application/datatables.wrapper': {
'datatables': 'datatables'
},
}
};
My wrapper script looks like the following (file "js/modules/application/datatables.wrapper.js")
define(
// require jQuery, DataTables, and the DataTables configuration object
// which resides in another file
["jquery", "datatables", "application/config/datatables.config"],
function($, dataTable, config) {
// Set datatables default values
$.extend(
dataTable.defaults,
config
);
return dataTable;
}
);
As odd as the mapping
'datatables': 'datatables'
may look, it's working like a charm!

Related

How to inject a dependency on an external script

I'm using a cloud service called Parse in a JavaScript closure injected with require.js:
define([/*some dependencies*/],
function(/*some dependencies*/) {
...
// using Parse. For example:
var TestObject = Parse.Object.extend("TestObject");
...
}
The tutorial for using Parse in Javascript instructs to include their script in the HTML header:
<script src="//www.parsecdn.com/js/parse-1.4.2.min.js"></script>
I don't want the HTML page to depend on Parse and I don't want to clutter the HTML with external scripts. Instead, I would like to require parse-1.4.2.min.js directly from the script. What's the proper way of doing so? How do I define this dependency and make it work?
Similar to jQuery, Parse adds itself to the global scope on load. If no other scripts depend on it, it can simply be included as a dependency in a module or require call.
require([
'https://www.parsecdn.com/js/parse-1.4.2.min.js',
], function () {
console.log(Parse);
}
If you are using any other non-AMD scripts that depend on Parse (or any other library) you will need to use a config/shim. It tells requireJS what order it should load the scripts, based on their dependencies.
E.g. when using a jQuery plugin, you wouldn't want it to load and execute before jQuery itself.
A config/paths setup also helps organise your project by allowing script locations to be defined in a single location and then included by reference.
See the requireJS docs for more info.
The following config/require successfully loads Parse and a fictional plugin:
require.config({
// define paths to be loaded, allows locations to be maintained in one place
paths: {
parse: 'https://www.parsecdn.com/js/parse-1.4.2.min',
plugin: 'example-non-amd-parse-plugin.js'
},
// define any non-amd libraries and their dependancies
shim: {
parse: {
exports: 'Parse'
},
plugin: {
exports: 'plugin',
deps: ['parse']
}
}
});
require(['parse'], function(Parse){
console.log(Parse);
});
// note Parse is not required
require(['plugin'], function(plugin){
// Parse is available as it is depended on by plugin and on the global scope
console.log(Parse, plugin);
});
So after all I just wrote this and it seems to work. Don't know what the problem was in the first place...
define(["https://www.parsecdn.com/js/parse-1.4.2.min.js"],
function () {

How can I use a local file during Require.js optimisation, but a CDN-hosted version at runtime?

My page includes several components that exist as separate AMD modules. Each of these components is turned into a single file by the Require.js optimiser. Because several of these components share dependencies (e.g. jQuery and d3), the optimiser paths config uses CDN URLs for those dependencies, rather than bundling them into the optimised file.
Here's where it gets tricky. I've written a module loader plugin for Ractive.js called rvc.js, which allows me to include Ractive components that are defined in HTML files. (Yes, I'm asking for help on how to use my own library.)
This works fine - code like this gets optimised as you'd expect:
define( function ( require ) {
var ChartView = require( 'rvc!views/Chart' );
var view = new ChartView({ el: 'chart' });
});
Because Ractive is used by several of the components, it should be served from a CDN like jQuery and d3. But it's used by rvc.js during the optimisation process, which means that the Ractive entry for the optimiser's paths config can't point to a CDN - it has to point to a local file.
Is there a way to tell Require.js 'use the local file during optimisation, but load from CDN at runtime'?
So here's the solution I eventually settled on. It feels somewhat kludgy, but it works:
Stub out the loaders and the library you don't want bundled
Add an onBuildWrite function that rewrites modules depending on the library, so that they think they're requiring something else entirely - in this case Ractive_RUNTIME
Add an entry to your runtime AMD config's paths object, so that Ractive_RUNTIME points to the CDN
My optimiser config now looks like this:
{
baseUrl: 'path/to/js/',
out: 'build/js/app.js',
name: 'app',
optimize: 'none',
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive'
},
stubModules: [ 'amd-loader', 'rvc', 'Ractive' ],
onBuildWrite: function ( name, path, contents ) {
if ( contents === "define('Ractive',{});" ) {
// this is the stub module, we can kill it
return '';
}
// otherwise all references to `Ractive` need replacing
return contents.replace( /['"]Ractive['"]/g, '"Ractive_RUNTIME"' );
}
}
Meanwhile, the script that loads the app.js file created by the optimiser has a config entry that points to the CDN:
require.config({
context: uniqueContext,
baseUrl: baseUrl,
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive',
'Ractive_RUNTIME': 'http://cdn.ractivejs.org/releases/0.3.9/Ractive.min'
}
});

Adding RequireJS module that uses jquery on a page that already has jquery as a global

I have an add-on to an application (call it appX) that allows users to create their own customizations using javascript, css and appX's webservices api.
Usually customizations are small and do not involve a lot of external libraries/plugins but when they do have external libs the typical users' library of choice is jQuery.
In the next version of appX they are using jQuery natively which I know is going to break some of the customizations.
So I have a need to modularize this situation. I have some other problems that are coming up and RequireJS seems like a good solution to these issues. I just need to figure out how to apply RequireJS properly for this situation
In my POC I'm loading require.js as follows:
<!--A bunch of other script including jQuery (but not require) are loaded already -->
<script type="text/javascript" src="/custom/js/require.js"></script>
<script type="text/javascript" src="/custom/js/dostuff.js"></script>
We'll call the jQuery loaded with appX jqueryx and the one I want to load jqueryp (p for private)
jQuery utilizes AMD and by default uses this definition internally:
define( "jquery", [], function () { return jQuery; } );
But in this case RequireJS is loaded AFTER jQuery (jqueryx) so there will be no default 'jquery' definition correct?
Some more background before I show you my problem... the file structure is like this:
appx
/js:
jqueryx.js
other.js
appx
/custom/js:
jqueryp.js
dostuff.js
Looking at the RequireJS api it seems that I should be doing something like this:
require.config({
baseUrl : 'custom/js',
paths : { 'jquery' : 'jqueryp'},
map: {
'*': { 'jquery': 'jquery-private' },
'jquery-private': { 'jquery': 'jquery' }
}
});
define(['jquery'], function (jq) {
return jq.noConflict( true );
});
require(['jquery'], function(jq){
console.log(jq.fn.jquery);
});
But when I do this I get an error:
Mismatched anonymous define() module: function (jq)...
I've played around with switching references to jquery, jquery-private as it's kind of confusing but with no progress.
How should I be applying RequireJS to this situation?
Almost a year late but better than no answer at all...
The following part should be moved into a "jquery-private.js" file:
define(['jquery'], function (jq) {
return jq.noConflict( true );
});
You can't define a module in your main entry point. Even if you could, the module has no name so how would you reference it?

requirejs - combine several files to single js file that not dependent on requirejs

I am writing jQuery plugin that contains a lot of code. Therefore I decided to separate the code and make it more modular for me (the developer). For this I use require.js.
Now I have 6 js files:
utils.js
base-row.jas
a-row.js
b-row.js
my-table.js
main.js
Files 1 to 5 defines JavaScript "classes" and they have dependecies between themselves. The "primary" class that operates all the concert is my-table.js. main.js has dependency only to my-table.js and creates a plugin from it:
require([
'my-table'
], function(MyTable) {
jQuery.fn.myTable = function(options) {
var table = new MyTable(this, options);
this.data('myTable', table);
return this;
};
});
Now I want to create from those files one big js file that contains all the 6 files without any dependency (except jQuery that the user should put reference to it). For this I used r.js (http://requirejs.org/docs/optimization.html) and as a result I got one big js file that depend on require.js (and contains calls to define and require). I followed this: http://requirejs.org/docs/faq-optimization.html#wrap and used almond.js in order to combined all my files for usage that is not dependent on require.js. This works fine.
The problem is why do I need all the define and require method calls and almond.js? Why couldn't the optimizer concatenate only the function results (as describe in this question: Why do concatenated RequireJS AMD modules need a loader?) like this:
(function() {
var utils = «function() {
....
return Utils;
}»();
var baseRow = «function(A) {
....
return BaseRow;
}»(utils);
....
....
var myTable = .....
//<--This is require call and therefore doesn't return a thing
(function(MyTable) {
jQuery.fn.myTable = function(options) {
var table = new MyTable(this, options);
this.data('myTable', table);
return this;
};
})(myTable);
})();
As a result of this process, I decided to check things out and combined manually all the files to one minified file. I end up with a file smaller by 3k then the almond version!
I don't find the logic behind the r.js optimizer creating require.js dependent result. In my case, no one will need to use any of the files, my primary js file is the only consumer. What do you think?
The default value for the findNestedDependencies option in the optimizer is "false", meaning that even after the scripts are optimized, there could still be a nested require or define call that would require a module loader. A loader is also needed for external dependencies.
I agree, however, that if findNestedDependencies is set to "true" and no external dependencies are part of the project, the optimizer should be able to remove its need for a loader.
You can just include the files needed in the file. You can do that with a getScript call.
$.getScript("my_lovely_script.js", function(){
//whatever you want here.
});

require.js loads dependencies incorrectly

So this is the setup, my base file is main.js which defines the scripts that are needed on all pages of the site I'm building. It looks like this:
define([
'/javascript/requirePlugins/require-order.js!http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js',
'/javascript/requirePlugins/require-order.js!/javascript/jquery-global-plugins.js',
'/javascript/requirePlugins/require-order.js!/javascript/globals.js'
], function () {
loadFonts();
}
);
It loads jQuery, some plugins and the globals script file. On one page I'm trying to load a jQuery plugin, but the plugin tries to load before jQuery is loaded. It looks like this:
require(['/javascript/requirePlugins/require-order.js!/main','/javascript/requirePlugins/require-order.js!/javascript/3rdparty/lemon-slider-0.2.js'], function () {
$j('#carousel<%= ClientID %>').lemmonSlider({loop:false});
});
The function doesn't appear to be following the order requested. I'm not sure I can even nest ordered functions like this. I've also tried just applying jQuery as a dependency, but this also fails:
require(['/javascript/requirePlugins/require-order.js!/jquery','/javascript/requirePlugins/require-order.js!/javascript/3rdparty/lemon-slider-0.2.js'], function () {
$j('#carousel<%= ClientID %>').lemmonSlider({loop:false});
});
Any suggestions to where I'm doing this wrong is appreciated, thanks
order plugin is removed and you may try shim config to load plugins in order
requirejs.config({
paths: {
'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min',
'bootstrap': '../bootstrap/js/bootstrap.min',
'select2': 'vendor/select2',
'jshashtable': 'vendor/jshashtable-2.1',
'jquery.numberformatter': 'vendor/jquery.numberformatter-1.2.3.min',
'jq-datepicker': 'vendor/bootstrap-datepicker',
'jq-datepicker.da': 'vendor/bootstrap-datepicker.da'
},
// Use shim for plugins that does not support ADM
shim: {
'bootstrap': ['jquery'],
'select2': ['jquery'],
'jq-datepicker': ['jquery'],
'jshashtable': ['jquery'],
'jquery.numberformatter': ['jquery', 'jshashtable']
},
enforceDefine: true
});
EDIT:
require-jquery is also no more maintaining.
The order plugin is useful if you just have a few top-level scripts you wanted loaded in order and those scripts do not use the module API supported by requirejs. It does not work out so well if you mix it/use it to load modules that do use the define() module API.
In particular, order just makes sure the script gets loaded first. However, the define() API specifies other scripts to load, and the order plugin does not know to wait for those scripts to load.
For this particular problem, I suggest using require-jquery.js as sinsedrix suggested. That, or wrap the scripts you use in define() calls. volo can help you do this with its amdify command:
volo.js amdify path/to/lemon-slider-0.2.js depends=jquery
Also, I would set the baseUrl and use "module naming" for the dependencies instead of full paths. This will allow the optimizer to work correctly. You can also map 'order' to the requirePlugins path, which helps cut down some of the line noise. I would also create a 'jquery' paths entry so that if you do wrap the other plugins in define calls, it will map back to the jquery loaded in your main.js file. So, in the top level script for your page:
requirejs.config({
baseUrl: '/javascript/',
paths: {
order: 'requirePlugins/require-order',
jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min'
}
});
Then your main.js can be written like so:
define([
'order!jquery',
'order!jquery-global-plugins',
'order!globals'
], function () {
loadFonts();
}
);
Note that here, the order usage is fine, as long as those dependencies do not call define() themselves.
But if you are wrapping the scripts you use in define calls, then you can get rid of the order! usage above. Keep the jquery paths config though.
Maybe you should try tu use the require-jquery : http://requirejs.org/docs/jquery.html
Then you won't have to worry about when jquery is loaded.

Categories

Resources