AMD-style browser loading support

900 views
Skip to first unread message

Andrew Nicolaou

unread,
Feb 14, 2012, 10:59:46 AM2/14/12
to d3-js
Hi,

I notice that d3 has a "better-node-package" branch to allow you to
just require("d3") when using node.

Is there an equivalent mechanism for use with AMD-style module loaders
in the browser (e.g. requirejs [1], curl.js [2])?

I'm using curl.js on a project and ideally I'd like to list d3 as a
module dependency like this:

/* my module */
define(['d3/layout'], function (d3) {
// use d3 here
// d3/layout will have loaded d3 as a dependency
});

I'd like to work on a patch but don't want to duplicate work if this
is already possible.

Thanks,
Andrew

[1] http://requirejs.org/
[2] https://github.com/cujojs/curl

Mike Bostock

unread,
Feb 14, 2012, 11:14:58 AM2/14/12
to d3...@googlegroups.com
The approach I would recommend on the browser side would be to build
d3.custom.js and load a single file, rather than loading multiple
files. I like having separable units of code from an architecture
standpoint, but I find managing separate files to be a pain.

I haven't used requirejs or curl.js before. It seems like lazy loading
of lots of small files would have undesirable performance
characteristics?

Mike

Andrew Nicolaou

unread,
Feb 14, 2012, 11:29:55 AM2/14/12
to d3...@googlegroups.com
I think that the AMD-style allows you to have both the architecture and performance benefits.

From an architecture point of view, you have modules that express dependencies between each other, can be lazy loaded and don't pollute the global namespace.

For performance in a production environment, requirejs has an optimiser that follows the dependencies between modules and creates a single file containing them all. 

The code calling these modules doesn't change as requirejs (or any AMD-compatible loader) will recognise that the modules have already been loaded and won't try to load them again.

I might have a go at implementing this on a fork. It might turn out to be a mess, in which case I'll try your approach. :-)

Thanks,
Andrew

Sergei Dorogin

unread,
Feb 20, 2012, 12:48:04 PM2/20/12
to d3...@googlegroups.com
+1
I'd like to add my 2 cents - AMD support is highly desirable in D3.
Your point "loading a single file is better then multiple files" is obviously right. But you mix production time and design time. AMD modules are mostly for making clientside development as good as serverside one. For production we have build systems - requireJS has build system (r.js) based on node.js which produce clean one or several script files. So you have module system for development and optimized scripts for production. I believe AMD support is a must-have feature for all mature libraries nowadays. Moreover it's very easy to add:
 
    if (typeof define === "function" && define.amd) {
        define([], function () {
            return d3;
        });
    }

I'd definitely recommend you to take a look at requireJS.

Frank van Gemeren

unread,
Feb 22, 2012, 5:40:46 AM2/22/12
to d3-js
I'd also like to request AMD support.

Chris Viau

unread,
Feb 22, 2012, 11:17:46 AM2/22/12
to d3...@googlegroups.com
We use D3 with RequireJS, and the wrapping is only a one-liner:

This is the file d3.AMD.js:
define(['require/order!d3/d3'], function() { return d3; });

And this is d3.layout.AMD.js:
define(['require/order!d3/d3', 'require/order!d3/d3.layout'], function() { return d3.layout; });

Then you can load them as usual. This is main.js:
require.config({
    baseUrl: './js',
    paths: {
        D3: 'd3/d3.AMD',
        D3_layout: 'd3/d3.layout.AMD'
    }
});

require(['./app'], function(App) {
    App.init();
});

And an example of use:
define([
    'D3',
    'D3_layout'
],
function (d3, d3_layout) {
   ...
});

Andrew Nicolaou

unread,
Feb 27, 2012, 4:55:13 AM2/27/12
to d3-js
Hi Chris,

I'm currently doing something similar. I've defined a wrapper package
called "d3" that loads all the d3 packages in order.

Whilst this does the job, it would be great if d3 detected the
availability of an AMD-compatible loader and used that so the extra
layer of indirection wouldn't be required.

Francesco Occhipinti

unread,
Mar 7, 2013, 5:12:34 AM3/7/13
to d3...@googlegroups.com

Hi, one year has elapsed since the last replies to this post. Probably requirejs has evolved, in the meantime.

I faced the same problem but i fixed it easily without a wrapper package, using requirejs shim. Using the shim, you can export a variable, and you can also define dependencies:

    d3: { exports: 'd3' },
    nvd3: {
      exports: 'nv',
      deps: ['d3']
    },

In this way, i simply install d3 and other packages with bower, and include them with require, and it is really quick and clean.

Nonetheless, i faced the following problem: there are probably some clashes between the global d3 variable and the local one (the one injected in the requiring modules). It is a d3 / require / nvd3 integration problem related to transitions and selections. I don't fully understand the problem, but i can already make some considerations.

- jquery has the same problem with require, and they provide the noconflict method to fix it
- many libraries have this behavior, they export a global symbol, but as far as i know there is no ready fix from requirejs for the general problem
- the problem is fixed if i rename all global references to `d3` into d3 source to another name. I still have `d3` in the injected module, but it is not conflicting anymore

I'm posting this here to show an easier way to include d3, and to ask your help about the global symbol. Many d3 functionalities work this way, but one of the nvd3 charts has transitions broken probably because a selection or dispatcher is overwritten. It requires deep understanding of d3 internals to spot precisely the error, but probably a simple yet correct handling of the global symbol will clear the whole tally of similar problems.

Francesco Occhipinti

unread,
Mar 7, 2013, 5:52:53 AM3/7/13
to d3...@googlegroups.com

Other info about this complex topic. Probably, due to the way requirejs handles shim dependencies, the global d3 symbol is exposed to nvd3. The same symbol, anyway, is not available to requiring modules, and will be overwritten somehow if injected (included in the module dependencies).

I tried also to wrap d3 in a module and properly return a local d3 variable, but looks like the problem still persists. This is a problem with require `shim` feature, nvd3 intialization and d3 relying on the global variable, quite a big mess! Anyway maybe some of you has some knowledge which can help with this.

Francesco Occhipinti

unread,
Mar 7, 2013, 6:07:39 AM3/7/13
to d3...@googlegroups.com
Since this is a multi-topic question, i posted it to stack overflow also http://stackoverflow.com/questions/15269535/requirejs-d3-and-nvd3-integration
Reply all
Reply to author
Forward
0 new messages