I'm attempting to load the Highcharts library using a shim in RequireJS. However, when Highcharts loads, it throws an exception because it can't access the jQuery methods it depends on.
The require config looks like so:
require.config({
baseUrl: "js",
shim: {
'libs/highcharts/highcharts.src.js': {
deps: ['jquery'],
exports: function(jQuery)
{
this.HighchartsAdapter = jQuery;
return this.Highcharts;
}
}
}
});
The exception that is thrown is:
Uncaught TypeError: undefined is not a function
and is in regards to this line:
dataLabels: merge(defaultLabelOptions, {
The issue is the merge call, which eventually maps itself back to jQuery (or some other adapter that Highcharts supports; but I'm just using jQuery).
I'm not sure exactly how to make sure Highcharts gets access to jQuery using RequireJS and shim.
Has anyone used RequireJS and Highcharts together before? I guess the issue isn't specific to highcharts, but any library that has other sorts of dependencies.
Thanks in advance for any advice or points to the correct direction!
To add further context, in hopes that someone who is familiar with require.js or shims will be able to help without having to be too intimately familiar with highcharts, here's some source that sets up this merge method in Highcharts
var globalAdapter = win.HighchartsAdapter,
adapter = globalAdapter || {},
// Utility functions. If the HighchartsAdapter is not defined,
// adapter is an empty object
// and all the utility functions will be null. In that case they are
// populated by the
// default adapters below.
// {snipped code}
merge = adapter.merge
// {snipped code}
if (!globalAdapter && win.jQuery) {
var jQ = win.jQuery;
// {snipped code}
merge = function () {
var args = arguments;
return jQ.extend(true, null, args[0], args[1], args[2], args[3]);
};
// {snipped code}
}
The win object is a reference set up to window at the beginning of the script. So, I thought adding window.jQuery = jQuery; to the export method on the shim would result in highcharts picking up the jQuery reference; but it didn't.
Again, any insight, info, advice, or heckles would be appreciated at this point - I'm at a complete loss, and starting to question whether trying to implement and AMD package system in browser javascript is even worth it.
After accepting the answer from pabera below I thought it appropriate to update my question to reflect how his answer helped my solution (though, it's basically his answer).
RequireJS uses "paths" to find libs that aren't "AMD" supported and loads them on your page. the "shim" object allows you to define dependencies for the libraries defined in paths. The dependencies must be loaded before requirejs will try to load the dependent script.
The exports property provides a mechanism to tell requirejs how to determine if the library is loaded. For core libs like jquery, backbone, socketio, etc they all export some window level variable (Backbone, io, jQuery and $, etc). You simply provide that variable name as the exports property, and requirejs will be able to determine when the lib is loaded.
Once your definitions are done, you can use requirejs' define function as expected.
Here's my example require.config object:
require.config({
baseUrl: "/js/",
paths: {
jquery: 'jquery',
socketio: 'http://localhost:8000/socket.io/socket.io', //for loading the socket.io client library
highcharts: 'libs/highcharts/highcharts.src',
underscore: 'libs/underscore',
backbone: 'libs/backbone'
},
shim: {
jquery: {
exports: 'jQuery'
},
socketio: {
exports: 'io'
},
underscore: {
exports: '_'
},
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
},
highcharts: {
deps: ['jquery'],
exports: 'Highcharts'
}
}
});
As pabera mentioned before, this is for Require.JS version 2.0.1.
I hope someone gets some use out of this; I know it road blocked me for a little while; so hopefully we kept you from banging your head into the same spot in the wall that we did, by posting this.
I had the exact same problem and I was struggling around many hours until I saw your entry here. Then I started over from scratch and now it works for me at least.
requirejs.config({
baseUrl:'/js/',
paths:{
jquery:'vendor/jquery',
handlebars: 'vendor/handlebars',
text: 'vendor/require-text',
chaplin:'vendor/chaplin',
underscore:'vendor/underscore',
backbone:'vendor/backbone',
highcharts: 'vendor/highcharts'
},
shim: {
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
underscore: {
exports: '_'
},
highcharts: {
exports: 'Highcharts'
}
},
});
Since I use Chaplin on top of Backbone, I am including some more files in my paths attribute. Highcharts has a similar structure to Backbone so I thought I could load it the same way. It works for me now. As you can see, I am introducing highcharts in the paths attribute already to export it as a shim afterwords.
Maybe this helps, otherwise let's try to contribute on it even more to solve your problem.
Although jQuery can be used as an AMD module it will still export itself to the window anyway so any scripts depending on the global jQuery or $ will still work as long as jQuery has loaded first.
Have you tried setting a path? jQuery is an interesting one because although you're encoruaged not to name your modules by the RequireJS documentation, jQuery actually does.
From the jQuery source
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
define( "jquery", [], function () { return jQuery; } );
}
What that means is you will need to tell RequireJS where to find 'jquery'. So:
require.config({
paths: {
'jquery': 'path/to/jquery'
}
});
If you're interested in why jQuery registers itself this way then there is a pretty large comment in the source which goes into more detail
Related
My JS code is Backbone.js based, so I think it is a good idea to separate "classes" with this logic as shown on picture (though I'm not sure where to place templates - in this packages or in global templates folder, and do not mind main.js - it is not related to CommonJS packages) :
Now since there is fairly lot of them - I've decided to use require.js to deal with this bunch of <script src=... tags but got stuck with app.js config file (which is the only one that I include like this -
<script data-main="/static/js/app.js" src="/static/js/libmin/require.js"></script>
What do I mean with stuck - of course I can iterate all this js files in require statement using names like PlayerApp/PlayerAppController.js, or using paths directive (not sure if it will make the code look not that ugly), but it would be cool if I can use something like python's from package import *, and of course there is no such thing in require.js.
The most similar thing is packages directive, but seems like it allows you to import only main.js from each package, so then the question is - what is the most correct way to load other files of concrete package from CommonJS's main.js? I have even found a way to determine current .js file's name and path - like this, and given that I can make up other files names in current package(if I will keep naming them with the same pattern), but still do not know how to import them from main.js
Edit:
There might be an opinion that it is not very clear what exactly am I asking, so let me get this straight: how on Earth do I import a huge amount of javascript files with that project structure in the most nice way?
You are mis-understanding the purpose of a module loader. require.js is not there to make it easy for you to import all of your packages into the current namespace (i. e. the browser). It is there to make it easy to import everything you need to run app.js (based on your data-main attribute). Don't try to import * - instead, just import thingYouNeed.
Configuration
What you will want to do is set up your require.config() call with all the necessary paths for libraries like Backbone that don't support AMD and then update your code to explicitly declare its dependencies:
require.config({
// Not *needed* - will be derived from data-main otherwise
baseUrl: '/static/js/app',
paths: {
// A map of module names to paths (without the .js)
backbone: '../libmin/backbone',
underscore: '../libmin/underscore',
jquery: '../libmin/jquery.min',
jqueryui.core: '../libmin/jquery.ui.core'
// etc.
}
shim: {
// A map of module names to configs
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
},
underscore: {
exports: '_'
},
jquery: {
exports: 'jQuery'
},
// Since jQuery UI does not export
// its own name we can just provide
// a deps array without the object
'jqueryui.core': ['jquery']
}
});
Dependencies
You will want to update your code to actually use modules and declare your dependencies:
// PlayerAppModel.js
define(['backbone'], function(Backbone) {
return Backbone.Model.extend({modelStuff: 'here'});
});
// PlayerAppView.js
define(['backbone'], function(Backbone) {
return Backbone.View.extend({viewStuff: 'here'});
});
// PlayerAppController.js
define(['./PlayerAppModel', './PlayerAppView'],
function(Model, View) {
// Do things with model and view here
// return a Controller function of some kind
return function Controller() {
// Handle some route or other
};
});
Now, when you require(['PlayerApp/PlayerAppController'], function(Controller) {}) requirejs will automatically load jQuery, underscore, and Backbone for you. If you never actually use mustache.js then it will never be loaded (and when you optimize your code using the r.js compiler, the extra code will be ignored there as well).
I'm trying to get an app running that uses both AngularJS and RequireJS. I'm having problems getting my OpenLayers lib to work in this setup.
I set the main AMD-modules in the main.js:
require.config(
{
baseUrl: 'scripts',
paths: {
// Vendor modules
angular: 'vendor/angular/angular',
openLayers: 'vendor/openlayers-debug',
other modules.....
},
shim: {
angular: {
exports: 'angular'
},
openLayers: {
exports: 'OpenLayers'
},
other modules....
}
}
);
require(['openLayers',
'angular',
'app',
'controllers/olMapController',
'directives/olMap',
other modules...
], function(OpenLayers) {
return OpenLayers;
}
);
Then in the angular controller I use for the initialisation of OpenLayers, I try to indicate that openlayers-debug.js is a dependency:
define(['openLayers'],
function(OpenLayers) {
controllers.controller('olMapController', ['$scope', function(scope) {
console.log('Loaded olMapController. OpenLayers version: ' + OpenLayers.VERSION_NUMBER);
}]);
}
);
Well, this doesn't work. SOMETIMES the olMapController function is executed, but mostly not. The console then just displays an error stating:
Error: [ng:areq] Argument 'olMapController' is not a function, got undefined
So, I think OpenLayers hasn't finished loading yet, but somehow require thinks it has and continues loading code that depends on OpenLayers, in this case the olMapController. It then can't find its dependency, whereupon Angular returns this error message. Or something like that...? Sometimes something happens that makes OpenLayers load fast enought for it to be present when it is loaded as a dependency. What that is, I can't tell.
I left out other libraries and modules require and define to keep the code readable. I hope the example is still understandable.
Any ideas on what I could do to get openlayers to load well? The error message disappears when I leave the ['openLayers'] dependency out of olMapController.
Thanks in advance for any help.
Best regards,
Martijn Senden
Well, just for reference my final solution. The comment by angabriel set me off in the right direction.
Like i said, I'm using the domReady module provided by RequireJS to bootstrap Angular. This is still being called to early, because OpenLayers isn't loaded yet when angular starts. RequireJS also provides a callback property in require.config. This is a function that is triggered when all the dependencies are loaded. So I used that function to require the angular bootstrap module. My config now looks like this:
require.config(
{
baseUrl: 'scripts',
paths: {
// Vendor modules
angular: 'vendor/angular/angular',
domReady: 'vendor/domReady',
openLayers: 'vendor/openlayers-debug',
etcetera....
},
shim: {
angular: {
deps: ['jquery'],
exports: 'angular'
},
openLayers: {
exports: 'OpenLayers'
},
},
deps: ['angular',
'openLayers',
'app',
'domReady',
'controllers/rootController',
'controllers/olMapController',
'directives/olMap'
],
callback: function() {
require(['bootstrap']);
}
}
);
And the bootstrap module looks like this:
define(['angular', 'domReady', 'app'], function(angular, domReady) {
domReady(function() {
angular.bootstrap(document, ['GRVP']);
});
});
Thanks for the help. #angabriel: I upvoted your comment. It's not possible to accept a comment as an answer, is it? Your initial answer wasn't really the answer to my question, but the comment helped a lot...
Regards, Martijn
Sorry this answer will only contain text as your code is too big for a small example.
I would suggest to write a directive that utilizes head.js to load libraries you need at a specific context. One could also think of a directive that initializes openLayers this way.
I guess your error is a timing problem, because the require.js and angular.js module loading gets out of sync - more precisely there seems to be no sync at all between them.
update
To repeat my comment which lastly helped to lead the OP in the right direction:
You have to decide which framework gives the tone. If you go with requireJS, then require everything and bootstrap angular manually. (Remove ng-app="" from index.html) and do `angular.bootstrap()ยด when requirejs has completed. So the problem most likely is, that angular starts too early.
So, after clicking on a button I want to display a Lightbox containing a link to youtube.
I use requirejs and lightbox in my project, but I get an error:
"Uncaught TypeError: Object function (e,t){return new x.fn.init(e,t,r)} has no method 'colorbox'"
I think the function doesn't find Colorbox, but I don't know why.
This is my file: openYoutubeLink:
define( [ 'modules/common/preferences' ], function ( preferences ) {
return function () {
$.colorbox({width:"900px", height:"600px", iframe:true, href:"youtube.de"});
};
});
Here a part of my main.js with the require.config:
paths: {
colorbox : 'libs/jquery/jquery.colorbox-min'
}
shim: {
'colorbox' : { deps: [ 'jquery' ], exports: 'jquery' }
}
I would make it so that the main module requires jQuery and the colorbox plugin. I'd require them so that I'm sure they are both loaded when my module executes. Currently, it looks like you have the global $ defined but this looks like happenstance rather than design.
So after modifications it would look like this:
define(['modules/common/preferences', 'jquery', 'colorbox'],
function (preferences, $) {
return function () {
$.colorbox({width:"900px", height:"600px", iframe:true, href:"youtube.de"});
};
});
Also, I do not believe this is the source of your immediate problem but I should mention that the exports here is wrong:
shim: {
'colorbox' : { deps: [ 'jquery' ], exports: 'jquery' }
}
You want to have a symbol which is specific to the module being shimmed. For instance, I use the cookie plugin for jQuery and shim it like this:
'jquery.cookie': {
deps: ["jquery"],
exports: "jQuery.cookie"
}
jQuery.cookie is a symbol which is defined if and only if the jquery.cookie file has loaded and executed properly.
(kryger suggested not defining exports. While it is true that the RequireJS documentation says that plugins like those of jQuery or Backbone don't need to have an exports defined, the same documentation then mentions that the detection of error conditions won't work if exports is not used. I consider the best practice to be always defining an exports rather than wait until eventual problems crop up.)
I'll use what I'm actually doing as an example. I have a knockout custom binding that depends on a jquery plugin that itself depends on jQuery UI that of course depends on jQuery. There is another file that depends on another plugin, and another file that depends on jQuery UI, etc. In require.config.js I have:
shim: {
"jquery-ui": {exports: "$", deps: ["jquery"]},
"jquery-plugin1": {exports: "$", deps: ["jquery-ui"]},
"jquery-plugin2": {exports: "$", deps: ["jquery-ui"]}
}
This works, and then in corresponding files I may do:
define(["jquery-plugin1"], function ($) {
However, I could also do:
define(["jquery", "jquery-ui", "jquery-plugin1"], function ($) {
There is also the case where a file may depend on both plugins:
// which one?
define(["jquery-plugin1", "jquery-plugin2"], function ($) {
define(["jquery", "jquery-ui", "jquery-plugin1", "jquery-plugin2"], function ($){
There may also be other dependencies such as knockout custom bindings (which don't need to export anything) so I may end up with:
define(["jquery-plugin1", "model1", "model2",
"ko-custom1", "ko-custom2", "ko-custom3",
"jquery-plugin2"],
function ($, m1, m2) {
This file may also depend on jQuery UI (which depends on jQuery), but those are both loaded implicitly via the plugins.
My question is is it better to be explicit about all requirements (i.e. include jQuery and jQuery-UI in define) and possibly leave off the exports, or is the less-verbose nested dependency handling preferred?
This is a great question and becomes very relevant when using something like AngularJS dependency injection as those dependencies need to exist before the module is registered. So this won't work:
define(['angular'],function (angular) {
return angular.module('myModule', ['mySubmodule']);
});
// Error: [$injector:nomod] Module 'mySubmodule' is not available!
You need to define the AMD dependency as well:
define(['angular','./mySubmodule'],function (angular) {
return angular.module('myModule', ['mySubmodule']);
});
It might be subjective but I find it easier to reason about it by having each module define it's own dependencies explicitly and letting requireJS resolve them instead of leaving it up to faith that an out-of-scope module has already defined them which breaks modularity.
By doing this you also know that your AMD modules can be tested independently without rewiring the missing dependency.
I read the requirejs document from here API
requirejs.config({
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: ['underscore', 'jquery'],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: 'Backbone'
},
'underscore': {
exports: '_'
},
'foo': {
deps: ['bar'],
exports: 'Foo',
init: function (bar) {
//Using a function allows you to call noConflict for
//libraries that support it, and do other cleanup.
//However, plugins for those libraries may still want
//a global. "this" for the function will be the global
//object. The dependencies will be passed in as
//function arguments. If this function returns a value,
//then that value is used as the module export value
//instead of the object found via the 'exports' string.
return this.Foo.noConflict();
}
}
}
});
but i am not getting shim part of it.
why should i use shim and how should i configure, can i get some more clarification
please can any one explain with example why and when should we use shim.
thanks.
A primary use of shim is with libraries that don't support AMD, but you need to manage their dependencies. For example, in the Backbone and Underscore example above: you know that Backbone requires Underscore, so suppose you wrote your code like this:
require(['underscore', 'backbone']
, function( Underscore, Backbone ) {
// do something with Backbone
}
RequireJS will kick off asynchronous requests for both Underscore and Backbone, but you don't know which one will come back first so it's possible that Backbone would try to do something with Underscore before it's loaded.
NOTE: this underscore/backbone example was written before both those libraries supported AMD. But the principle holds true for any libraries today that don't support AMD.
The "init" hook lets you do other advanced things, e.g. if a library would normally export two different things into the global namespace but you want to redefine them under a single namespace. Or, maybe you want to do some monkey patching on a methods in the library that you're loading.
More background:
Upgrading to RequireJS 2.0 gives some history on how the order plugin tried to solve this in the past.
See the "Loading Non-Modules" section of This article by Aaron Hardy for another good description.
As per RequireJS API documentation, shim lets you
Configure the dependencies, exports, and custom initialization for
older, traditional "browser globals" scripts that do not use define()
to declare the dependencies and set a module value.
- Configuring dependencies
Lets say you have 2 javascript modules(moduleA and moduleB) and one of them(moduleA) depends on the other(moduleB). Both of these are necessary for your own module so you specify the dependencies in require() or define()
require(['moduleA','moduleB'],function(A,B ) {
...
}
But since require itself follow AMD, you have no idea which one would be fetched early. This is where shim comes to rescue.
require.config({
shim:{
moduleA:{
deps:['moduleB']
}
}
})
This would make sure moduleB is always fetched before moduleA is loaded.
- Configuring exports
Shim export tells RequireJS what member on the global object (the window, assuming you're in a browser, of course) is the actual module value. Lets say moduleA adds itself to the window as 'modA'(just like jQuery and underscore does as $ and _ respectively), then we make our exports value 'modA'.
require.config({
shim:{
moduleA:{
exports:'modA'
}
}
It will give RequireJS a local reference to this module. The global modA will still exist on the page too.
-Custom initialization for older "browser global" scripts
This is probably the most important feature of shim config which allow us to add 'browser global', 'non-AMD' scripts(that do not follow modular pattern either) as dependencies in our own module.
Lets say moduleB is plain old javascript with just two functions funcA() and funcB().
function funcA(){
console.log("this is function A")
}
function funcB(){
console.log("this is function B")
}
Though both of these functions are available in window scope, RequireJS recommends us to use them through their global identifier/handle to avoid confusions. So configuring the shim as
shim: {
moduleB: {
deps: ["jquery"],
exports: "funcB",
init: function () {
return {
funcA: funcA,
funcB: funcB
};
}
}
}
The return value from init function is used as the module export value instead of the object found via the 'exports' string. This will allow us to use funcB in our own module as
require(["moduleA","moduleB"], function(A, B){
B.funcB()
})
Hope this helped.
You must add paths in requirejs.config to declare, example:
requirejs.config({
paths: {
'underscore' : '.../example/XX.js' // your JavaScript file
'jquery' : '.../example/jquery.js' // your JavaScript file
}
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
},
'foo': {
deps: ['bar'],
exports: 'Foo',
init: function (bar) {
return this.Foo.noConflict();
}
}
}
});