requirejs unexpected behaviour - javascript

// conifg.js
require.config({
paths: {
'main': 'main',
'socketio': './libs/socket.io/socket.io',
'plotly': './libs/plotly/plotly-latest.min',
'renderDataToPlotly': './scripts/renderDataToPlotly',
'jquery': './libs/jquery/jquery-2.1.4.min',
'jqueryUI': './libs/jquery/jquery-ui-1.11.4.custom/jquery-ui.min',
'sliders': './scripts/sliders',
'makePlotlyWindowResponsive': './scripts/makePlotlyWindowResponsive'
},
shim: {
'jqueryUI': ['jquery']
}
});
require(['main']);
// main.js
define([
'jquery',
'jqueryUI',
'socketio',
'sliders',
'makePlotlyWindowResponsive',
'renderDataToPlotly'
],
function($, ui, io, sliders, makePlotlyWindowResponsive, renderDataToPlotly) {
//
}
);
// renderDataToPlotly.js and makePlotlyWindowResponsive.js
define(['plotly'], function() {
});
When I load the page I get this load order:
As you can see, makePlotlyWindowResponsive.js (1, on image) loads before plotly-latest.min.js (2, on image). As I understand requirejs mechanics, I would spect a Plotly is not defined error on makePlotlyWindowResponsive.js, but I'm not getting any. Everything works.
I want to understand requirejs and how it works.
Question 1: How there is not an error?
Question 2: That means that, despite load order, there is no error if files are loaded before page is fully loaded?
Thanks for your time!

The relative order you witnessed between plotly.min.js and the modules that depend on it is necessary. RequireJS has no reason to fetch plotly.min.js until makePlotlyWindowResponsive or renderDataToPlotly have been fetched.
Terminological note: I say "fetching (a module)" for the action of issuing an HTTP query on the network and I'll use "defining (a module)" for the action of running the factory function of a module. The term "loading" is too ambiguous.
What happens is:
You require main. So RequireJS fetches main.
RequireJS executes the define in main. The factory (the callback) cannot be run until the dependencies are defined themselves. So it initiates fetching the dependencies. This fetching can happen in any order. Among the dependencies are makePlotlyWindowResponsive and renderDataToPlotly.
RequireJS fetches makePlotlyWindowResponsive and renderDataToPlotly. (Their relative order does not matter.)
RequireJS executes the define of either makePlotlyWindowResponsive or renderDataToPlotly. This is where it learns that it must fetch the module plotly which resolves to ./libs/plotly/plotly-latest.min.js. Before this point, RequireJS has no idea that plotly will be needed. The fact that it is among the paths is not a sufficient condition to trigger its loading.
RequireJS fetches ./libs/plotly/plotly-latest.min.js.

When requirejs receives a script from the network, it runs that script. the require (or define?) function says "download these other scripts, then run them, and once you've got all their return values, run this function". So it waits for the other scripts to load and return their values before calling the function in this one. In short, the order in which they load may not be the same as the order in which their functions run.

Related

Using D3.JS and the sankey plugin in a require.js module

I'm trying to build a requirejs module giving client code the options to render stuff with d3.js. The first plugin I want to use is a sankey diagram.
My module so far:
define(['d3'], function(ignore) {
console.log("bef", d3);
require(['sankey.js']);
console.log("aft", d3);
d3.sankey();
return {
...
d3: d3,
renderSankey: function(options) {
...
}
}
The sankey.js script uses the global d3 variable and adds the function sankey(). (I tried both btw, define(['d3'], function(ignore) and define(['d3'], function(d3), exactly the same result).
The error: TypeError: d3.sankey is not a function, no matter if I try to call it directly as the code shows or like this.d3.sankey() in the renderSankey function.
The console output says (both times, before and after the require(...) call:
sankey: d3.sankey()
No matter what I try, it won't work. I feel like I missed something JS specific about shadowing, but why is there a sankey function, when I console.log the object and a row later, when I try to call I get an error? What am I doing wrong?
info:
I'm using this inside a splunk html dashboard, this is maybe important...
I don't want the client code to import the dependicies (with about 100 plugins to come, this would be a pain)
When I just copy the content of the sankey.js into my module, everything works fine
/edit: Here is the Require configuration (given by the Splunk Dashboard)
require.config({
baseUrl: "{{SPLUNKWEB_URL_PREFIX}}/static/js",
waitSeconds: 0 // Disable require.js load timeout
});
The require call you are using to load sankey is asynchronous. It will launch the loading of sankey but by the time require returns, sankey is not loaded. You should change your code to:
define(['d3', 'sankey'], function (d3) {
d3.sankey();
I take it that d3 also leaks the symbol d3 in the global space but AMD modules should not rely on global symbols unless these are part of the runtime environment (e.g. window, document).
You also need to set your RequireJS configuration to make sankey dependent on d3 because the define above does not by itself ensure that d3 will load before sankey. So you need this in your configuration:
shim: {
sankey: ['d3']
}
This makes sankey dependent on d3. (Note that shim can only be used to affect the loading of files that are not proper AMD module. sankey does not call define to register itself, and thus is not a proper AMD module, and we can use shim for it.)
Also, module names should generally not have .js in them so when you want to load the plugin, load it as sankey, not sankey.js.
Okay, I think #Louis and I just misunderstood each other. This may be caused by my own stupidity, since I wasn't aware that a configuration of require.js can be done anywhere (and not only once in the root file). How ever, to still get the Splunk specific part I post this answer (instead of accepting Louis'):
I added a new app to my splunk environment to (a viz app). I actually configure the dependencies first (in the by other splunk apps loadable d3-viz module):
require.config({
paths: {
'd3': '../app/D3_Viz/d3', // d3.js
'sankey': '../app/D3_Viz/sankey', // sankey.js
'XYZ': 'all the paths go here'
},
shim: {
'sankey': ['d3'],
'XYZ': ['d3'],
// all the dependecies go here
}
});
define(['splunkjs/ready!', 'jquery', 'd3'],
function(mvc, $, ignore) {
var d3Vis = {
...
renderSankey: function(options) {
// load dependencies dynamically
require(['sankey'], function() {
// actually render things
});
},
renderXYZ: function(options) {
require(['XYZ'], function() {
...
});
},
...
}
}
return d3Vis;
All my dependencies can be configured in the viz-app (and not in the client code using the app, this has been my fundamental missunderstanding of require.js); the only thing to do is loading the app/viz as a whole (in this example in a HTML dashboard:
require([
"splunkjs/mvc",
"splunkjs/mvc/utils",
"splunkjs/mvc/tokenutils",
"underscore",
"jquery",
"splunkjs/mvc/simplexml",
"splunkjs/mvc/headerview",
"splunkjs/mvc/footerview",
...
"../app/D3_Viz/viz"
],
function(
mvc,
utils,
TokenUtils,
_,
$,
DashboardController,
HeaderView,
FooterView,
...
d3Viz
){
... splunk specific stuff
// No dependencies have to be configured
// in the client code
d3Viz.renderSankey({...});
}
);

how to reset a requirejs module between unit tests

I have a JavaScript project which I want to observe the TDD methodology. I chose the karma framework and requirejs library for this and followed an example demonstrated in the karma docs here.
There is an example of one unit-test file, which is:
define(['app', 'jquery', 'underscore'], function(App, $, _) {
describe('just checking', function() {
it('works for app', function() {
var el = $('<div></div>');
var app = new App(el);
app.render();
expect(el.text()).toEqual('require.js up and running');
});
it('works for underscore', function() {
// just checking that _ works
expect(_.size([1,2,3])).toEqual(3);
});
});
});
The main problem with this approach is that there is no way to clear the App module for each test. So if there are some changes in the App (like closure variables changes etc.) in one it call, then they can affect other it call.
Could anyone suggest something on this? These are such popular tools, I can't believe no one ever ran into such a situation.
So, to recapitulate the question, I would like to ask for an answer for any of these more specific ones:
is there any way to "reset" (clear) module in requirejs? (actually, I suppose that there is no such a way, except reload all modules once again)
is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
With some help from Google I've found a solution to this. I cannot say that it is ideal, but it works at least and in each test a required module is in it's pristine state.
First, one need to have his main.js test file like this, it is almost the same as in the docs, except tests are not defined as modules and are not included as dependencies:
require.config({file
baseUrl: '/base',
paths: {},
shim: {},
deps: [],
callback: window.__karma__.start
});
In the main karma config must be present this (one need to adapt his own paths)
files: [
{pattern: 'src/**/*.js', included: false},
'tests/unit/**/*.js'
],
And in the tests themselves we leverage jasmine async tests capabilities and requirejs.undef method for "clearing" a module (which will reset the loader's internal state to forget about the previous definition of the module, as it is stated in the docs).
This method has one implication, that it will not reset other modules, which are already loaded and depend on the reset module, but this should not be a problem, if one writes his tests in a right way, that is he tests only one module (and may be some parent ones, from which the child inherits some behavior), so that it should not be hard to undef several modules at ones.
describe('Some/Module tests', function() {
'use strict'
var M
beforeEach(function(done) {
requirejs.undef('src/Some/GParentModule')
requirejs.undef('src/Some/ParentModule')
requirejs.undef('src/Some/Module')
require(['src/Some/Module'], function(m) {
M = m
done()
})
})
it('some description', function() {
var i = 0
for (var prop in M) {
i++
}
expect(i).toEqual(1)
expect(prop).toEqual('init')
expect(M.init).toEqual(jasmine.any(Function))
})
})
I've checked this, between it calls changes to the module do not persist, so I suppose, it's safe to use this approach.
Thanks everyone for help, if someone has a better way, please answer here.
is there any way to "reset" (clear) module in requirejs?
You can use requirejs.undef(), but the documentation mention some gotchas. And to my knowledge there is no call you can use to say "unload this module and everything it depends on", which in a complex application is probably what is needed.
is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
The better approach is to design your application so that state is tied to an object you instantiate rather than to the module itself. This is what I do in the test suite for a large application of mine. The beforeEach hook resets what requires resetting and instantiates the application anew for each test.

Calling require(['app']) Only Once

How can I call requireJS require(['app'], function() {}); only once at the beginning for the whole application so that any subsequent require(["..."], function(...){}); don't need to be wrapped within require(['app']?
This is my set up:
1) Load require.js
<script data-main="js/app.js" src="requirejs/require.min.js"></script>
2) Have app.js shims and basUrl configured properly.
requirejs.config({
baseUrl: "scripts/js",
paths: {
"jquery": "../bower_components/jquery/dist/jquery.min",
"modernizr": "../bower_components/modernizr/modernizr",
.
.
.
},
shim: {
"jquery.migrate": ['jquery'],
.
.
.
}
});
3) Dynamically load JS on different pages:
// Home Page
require(['app'], function() {
require(["jquery", "foundation", "foundation.reveal"], function ($, foundation, reveal){
$(document).foundation();
});
});
// Catalog Page
require(['app'], function() {
require(["jquery", "lnav/LeftNavCtrl","controllers/ProductCtrl", "controllers/TabsCtrl"], function ($, NavCtrl, ProductCtrl, TabsCtrl){
$(function() {
NavCtrl.initLeftNav();
});
});
});
Unless I wrap with require(['app'], function()) each time I call require("...") to load external JS or AMD modules, the app is not initialized and I get JavaScript errors. The above code works but it's not very efficient.
Is there a way to start my requireJS app before I try loading scripts?
I tried calling at the very beginning right after I load require.min.js:
require(["app"], function (app) {
app.run();
});
but it didn't work.
There are no provisions in RequireJS to ensure that a specific module is always loaded before any other module is loaded, other than having your first module load the rest. What you are trying to do is share your first module among multiple pages so it cannot perform the work of loading what is specific to each page.
One way you can work around this is simply to load app.js with a regular script element:
<script src="requirejs/require.min.js"></script>
<script src="js/app.js"></script>
Then the next script element can start your application without requiring app.js:
<script>
require(["jquery", "foundation", "foundation.reveal"], function ($, foundation, reveal){
$(document).foundation();
});
</script>
This is actually how I've decided to launch my modules in the applications I'm working on right now. True, it is not as optimized as it could be because of the extra network round-trip, but in the case of the applications I'm working on, they are still in very heavy development, and I prefer to leave this optimization for later.
Note that generally you don't want to use script to load RequireJS modules but your app.js is not a real module as it does not call define, so this is okay.
Another option would be to use a building tool like Grunt, Gulp, Make or something else and create one app.js per page and have each page load its own app.js file. This file would contain the configuration and the first require call to load the modules specific to your page.

Backbone.js + require.js : Wait until file is loaded

I'm using backbone.js and require.js. I have a script with files dependencies but the problem is that a file is not loaded before executing my script. So, a function is not defined. Here is the code exemple :
define([
'jquery',
'jqueryUi',
'holder',
'knob',
'jquery.ui.widget',
'iframeTransport',
'fileupload',
'knobScript',
], function($, ui, Holder) {
$(document).ready(function() {
$('#upload').fileupload({...}); // This one is unedefined because the script from the file fileupload is not completely loaded
});
});
Is someone has a solution to be sure that the script fileuplaod called in define is fully loaded before executing the script with the function (functionFromFileupload) ?
Thank for your help
To complement Evgeniy's comment: The problem is not that fileupload is loaded after your function. It will be loaded before running your function, that is the contract of Require. (If you can confirm it doesn't, then it would probably be a misconfiguration or less probably a bug of Require.)
Most probably the problem is that, sometimes, fileupload may be loaded before jQuery. Thus, it does not find the jQuery object to plug to and $(...).fileupload(...) fails. Use shim in the Require configuration, e.g. as:
require.config({
...
shim: {
fileupload: {
deps: ["jquery"]
}
...
}
...
});
You will probably have to shim other things too, e.g. jQueryUI.

Knockout with require.js - not looking for knockout in my configured 'paths' section

I’m developing a multi-page app, using requirejs to manage my javascript libs / dependencies.
My idea is that i'll have a main.js that holds the config, and then an .js file for each page that needs it, for example "register.js"
My require config is in javascripts/main.js
requirejs.config({
baseUrl: '/javascripts',
waitSeconds: 200,
paths: {
'async': 'lib/require.async',
'jquery': 'lib/jquery-1.7.2.min',
'knockout': 'lib/knockout-3.0.0'
});
I’ve got a knockout view model that looks like this:
javascripts/viewModels/userDetailsViewModel.js
define(['knockout'], function(ko) {
return function() {
var self = this;
self.name = ko.observable();
self.email = ko.observable();
});
My ‘entry point’ is javascripts/register.js
require(['./main', 'knockout', 'viewModels/userDetailsViewModel'], function(main, ko, userDetailsViewModel) {
ko.applyBindings(new userDetailsViewModel());
});
On my register.html page, i’ve got the script reference like this:
<script data-main="/javascripts/register" src="/javascripts/lib/require.js"></script>
When my page loads, I get these errors in the console:
GET http://localhost:3000/javascripts/knockout.js 404 (Not Found)
and
Uncaught Error: Script error for: knockout
I’m not sure why it’s looking for knockout.js - I’ve specified knockout in the paths section of my config, to look in lib/knockout-3.0.0
My dir structure is:
javascripts/
Most of my pages js files go here
javascripts/viewModels
Has knockout viewmodels
javascripts/lib
Contains knockout, jquery, requirejs etc...
The problem is that RequireJS will execute the call require(['./main', 'knockout', 'viewModels/userDetailsViewModel'] without a configuration. Yes, ./main is listed before knockout but there is no order guarantee between the dependencies passed in a single require call. RequireJS may load ./main first, or knockout first. And even if ./main were loaded first by this specific call, I believe it would not have any impact on how the other modules loaded by this call would load. That is, I think this require would operate on the basis of the configuration that existed at the time it was called, and that any configuration changes caused by the modules it loads would take effect only for subsequent require calls.
There are many ways to fix this. This should work:
require(['./main', function(main) {
require(['knockout', 'viewModels/userDetailsViewModel'], function(ko, userDetailsViewModel) {
ko.applyBindings(new userDetailsViewModel());
});
});
Or you might want to restructure your files and what you pass to data-main so that your requirejs.config is loaded and executed before your first require call. Here's an example of restructuring. Change your entry point to be /javascripts/main.js:
<script data-main="/javascripts/main.js" src="/javascripts/lib/require.js"></script>
Change /javascripts/main.js so that it contains:
requirejs.config({
baseUrl: '/javascripts',
waitSeconds: 200,
paths: {
'async': 'lib/require.async',
'jquery': 'lib/jquery-1.7.2.min',
'knockout': 'lib/knockout-3.0.0'
});
require(['knockout', 'viewModels/userDetailsViewModel'], function(ko, userDetailsViewModel) {
ko.applyBindings(new userDetailsViewModel());
});
And remove /javascripts/register.js. This would be one way to do it. However, it is hard for me to tell whether this would be what you want in your specific project, because I do not know the whole project. The way to restructure for your specific project really depends on what other pages might use RequireJS, what information is common to all pages, what is specific to each page, whether you use a template system to produce HTML, etc.

Categories