I am trying to modularize my Backbone app through Requirejs, but I cannot seem to get Requirejs to include Backbone. Here is my main.js that gets included from the index page:
require.config({
baseUrl: '/static/js/',
paths: {
jquery: 'libs/jquery/jquery-min',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-min',
text: 'libs/require/text',
},
});
require(['/static/js/views/app.js'], function(AppView) {
var appView = new AppView;
});
Here is my app.js that gets included from the above file:
define([
'jquery',
'underscore',
'backbone',
], function($, _, Backbone) {
console.log($);
console.log(_);
console.log("inside of the app js file");
var AppView = Backbone.View.extend({
initialize: function() {
console.log("inside of appview initialize");
},
});
});
The following information gets printed out in my Google Chrome console:
function (a,b){return new e.fn.init(a,b,h)}
app.js:7undefined
app.js:8inside of the app js file
app.js:9Uncaught TypeError: Cannot read property 'View' of undefined
The definition for $ works, but the definitions for _ and Backbone do not work. They come up as undefined. Any idea why this is happening?
I recommend you use the version of requireJS that has jQuery bundled. It will save you much setup pain. You can read the details here: http://requirejs.org/docs/jquery.html and download the files here: https://github.com/jrburke/require-jquery
In their own words:
With RequireJS, scripts can load in a different order than the order they are specified. This can cause problems for jQuery plugins that assume jQuery is already loaded. Using the combined RequireJS + jQUery file makes sure jQuery is in the page before any jQuery plugins load.
This should take care of loading jQuery properly with requireJS:
<script data-main="js/main" src="js/require-jquery.js"></script>
Main.js
A couple of notes here:
No need to re-define the path to jquery since that's already taken care of
You still have to indicate jquery as a required module
(I had to update the paths to have them work in my system)
Code:
require.config({
baseUrl: 'js/',
paths: {
underscore: 'libs/underscore-min',
backbone: 'libs/backbone-min',
},
});
require(['jquery', 'app'], function($, AppView) {
console.log("done w/ requires");
console.log($)
console.log(AppView)
var appView = new AppView;
});
app.js
A couple notes:
You can only retrieve the JS files after loading them in the callback if they have been defined as modules. So function($, _, Backbone) will fail for you.
You must return your object so that it can be used in the main.js callback (return AppView)
Code:
define(
[
'jquery',
'underscore',
'backbone',
],
function() {
console.log($);
console.log(_);
console.log(Backbone);
console.log("inside of the app js file");
return AppView = Backbone.View.extend({
initialize: function() {
console.log("inside of appview initialize");
},
});
});
Console
With that code in place, this is the console output:
function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
} [app.js:8]
function (a){return new m(a)} [app.js:9]
Object [app.js:10]
inside of the app js file [app.js:11]
done w/ requires main.js:21
function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
} [main.js:22]
function (){a.apply(this,arguments)} [main.js:23]
inside of appview initialize [app.js:15]
I would shy away from using forked versions of backbone and underscore. A "shim" configuration option has been added to requirejs to handle 3rd party libraries that don't natively support AMD. This makes updating to the latest versions of the libraries much easier.
http://requirejs.org/docs/api.html#config-shim
Here is an example:
require.config({
paths: {
jquery: "libs/jquery",
underscore: "libs/underscore",
backbone: "libs/backbone"
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
}
}
});
You may not have the correct reference backbone and _,may be this can help you:
Loading Backbone and Underscore using RequireJS
more:
http://requirejs.org/docs/api.html#config-shim
Related
This code is working fine on Chrome, IE and also android and IOS but an issue occurs sometimes on Firefox. After a few reload of the page the issue is gone!
Error: Module name 'underscore' has not been loaded yet for context: _
http://requirejs.org/docs/errors.html#notloaded require.js (line 7, col 217)
The code :
core.js
require.config({
paths: {
text: 'libs/text',
loader: 'libs/backbone/loader',
jQuery: 'libs/jquery/jquery',
Underscore: 'libs/underscore/underscore',
Backbone: 'libs/backbone/backbone',
'hammer': 'libs/jquery/hammer-min'
},
shim: {
Underscore: { deps: ['jQuery'] },
Backbone: { deps: ['Underscore'] }
}
});
require(['routers/core'], function (App) {
jQuery(document).ready(function () {
App.start();
});
});
routers/core.js
define([
'jQuery',
'Underscore',
'Backbone',
'routers/common'
], function ($, _, Backbone, CommonRouter) {
var start = function () {
var app = {
init: function () {
}
};
_.extend(CommonRouter.prototype, app);
var app_router = new CommonRouter;
Backbone.history.start();
};
return { start: start };
});
libs/backbone/loader.js
define([
'libs/jquery/jquery-base64',
'libs/jquery/jquery-easing',
'libs/jquery/jquery-inputmask',
'libs/jquery/jquery-mousewheel',
'libs/jquery/jquery-tagsinput',
'libs/jquery/jquery-validate',
'libs/jquery/highcharts',
'libs/jquery/raphael',
'libs/jquery/selectreplace',
'libs/jquery/touchswipe',
'libs/jquery/flowplayer-min',
'libs/underscore/underscore-min',
'libs/backbone/backbone-min'
],
function(){
return {
Backbone: Backbone.noConflict(),
_: _.noConflict(),
$: jQuery.noConflict()
};
});
The issue is that Backbone: Backbone.noConflict(), is undefined because the module "underscore" is not loaded yet.
How can I wait and be sure the modules required are loaded when I need them?
To answer your question:
How can I wait and be sure the modules required are loaded when I need them?
We can use the shim option of configuration for that.
To setup jQuery and underscore as dependencies of backbone you should do:
shim: {
backbone: { deps: ['jQuery','underscore'] }
}
You seem to have setup jQuery as a dependency of underscore where there is no actual dependency between them, which is unnecessary.
Apart from that you seems to have few issues -
First of all, you haven't specified any arguments to your libs/backbone/loader.js module. Since there is no global backbone or _ in window, unless you specify these your module won't have a way to access to the exports apart from the arguments object, unless somehow it's available in it's the parent scope.
Most of your imports seems to be jQuery plugins that just modifies jQuery. In such case you should specify them after all other libs that actually exports something which you want to use so that you don't have to specify an argument for each.
another issue could be that you're using the short path names to setup the shim option but you're using the full path (for eg libs/backbone/backbone-min) to load the libraries in libs/backbone/loader.js.
I wonder how it works for you in other browsers without specifying the import names as arguments.
define([
'underscore',
'jquery',
'backbone'
'libs/jquery/jquery-base64',
'libs/jquery/jquery-easing',
'libs/jquery/jquery-inputmask',
'libs/jquery/jquery-mousewheel',
'libs/jquery/jquery-tagsinput',
'libs/jquery/jquery-validate',
'libs/jquery/highcharts',
'libs/jquery/raphael',
'libs/jquery/selectreplace',
'libs/jquery/touchswipe',
'libs/jquery/flowplayer-min'
],
function(_,
$,
Backbone
/*all other imports in order*/
) {
return {
Backbone: Backbone.noConflict(),
_: _.noConflict(),
$: jQuery.noConflict()
};
});
side note: I modified the first letter of path names to small letter because I prefer having a consistent naming convention
I would like to load and use mustache by requirejs.
Maybe this question has already asked:
AMD Module Loading Error with Mustache using RequireJS
Anyway I am trying to figure out how can I fix my code:
main.js
require.config({
paths: {
jquery: 'libs/jquery/jquery',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-optamd3-min',
mustache: "libs/mustache/mustache"
}
});
require([
'views/app'
], function(AppView){
var app_view = new AppView;
});
app.js
define([
'jquery',
'underscore',
'backbone',
"mustache"
], function($, _, Backbone, Mustache) {
console.log($, _, Backbone, Mustache); // <-- *** Mustache is null ***
// ......
}
);
As you can see in the comment on app.js file, Mustache is null...
Should I use another library of Mustache?
Here what I am using Mustache
Looks like Mustache supports AMD modules as of July '12. So it should now work out of the box with a loader such as require.js.
You should just create in your mustache directory a new file mustache-wrap.js which looks like this:
define(['libs/mustache/mustache'], function(Mustache){
// Tell Require.js that this module returns a reference to Mustache
return Mustache;
});
and then your main will be:
mustache: "libs/mustache/mustache-wrap"
Not sure if RequireJS 2.1.0 was out at the time of posting this question (and the answers) but the preferred way of handling this now is using shim config element (more info on project's docs page).
Your main.js would become:
require.config({
paths: {
jquery: 'libs/jquery/jquery',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-optamd3-min',
mustache: "libs/mustache/mustache"
},
shim: {
'mustache': {
exports: 'Mustache'
}
}
});
(...)
That's effectively the same as wrapper suggested #AntoJs, but without the boilerplate code.
...but then, since Mustache supports AMD there's no need to wrap/shim in the first place!
You could probably also do in-line named define in the code that consumes mustache, or somewhere in "main.js" (saves the trouble of creating *-wrap file)
define('mustache', ['libs/mustache/mustache'], function(){
// Tell Require.js that this module returns a reference to Mustache
return Mustache; // from global
});
require(
['jquery','underscore','backbone','mustache']
, function($, _, BB, Mustache){
// use them
}
)
Here is my main.js before using domReady:
require.config({
paths : {
loader : 'libs/backbone/loader',
jQuery : 'libs/jquery/jquery-module',
Underscore : 'libs/underscore/underscore-module',
Backbone : 'libs/backbone/backbone-module',
templates : '../Templates'
}
});
require([ 'app' ], function(app) {
app.initialize();
});
And app.js:
define([ 'jQuery', 'Underscore', 'Backbone', 'router',
'services/Initializers/MainFrameInitializer',
'services/Initializers/FlowsViewsInitializer',
'services/Initializers/EditModuleInitializer',
'services/Sandboxes/ModulesNavigationSandbox',
'services/Sandboxes/ApplicationStateSandbox', 'DataModel/Constants' ],
function($, _, Backbone, Router, MainFrameInitializer,
FlowsViewsInitializer, EditModuleInitializer, ModulesNavigationSandbox,
ApplicationStateSandbox, Constants) {
var initialize = function() {
// Pass in our Router module and call it's initialize function
MainFrameInitializer.initialize();
FlowsViewsInitializer.initialize();
EditModuleInitializer.initialize();
ApplicationStateSandbox.startCheckStatus();
ModulesNavigationSandbox.navigate(Constants.Modules.Home);
// Router.initialize();
};
return {
initialize : initialize
};
});
All works fine until I optimize the project. I have figured out, that the script starts to run before the DOM is ready, something that was not true before the optimization. Anyway, I wish to use the domReady plugin to make sure the DOM is loaded first.
But, apparently, I have no idea how to do it correctly. Here is the new version of main.js:
require.config({
paths : {
loader : 'libs/backbone/loader',
jQuery : 'libs/jquery/jquery-module',
Underscore : 'libs/underscore/underscore-module',
Backbone : 'libs/backbone/backbone-module',
templates : '../Templates'
}
});
require([ 'domReady', 'app' ], function(domReady, app) {
domReady(app.initialize);
});
Very neat and very wrong, because app is loaded in parallel with domReady before the DOM is ready.
How do I fix it?
Thanks.
EDIT
I think I have understood our problem. The constructor functions of the app dependencies should not run any DOM dependent code. They should just return functions, capturing the DOM dependent logic. That logic should be executed from app.initialize, which is guaranteed to be run when the DOM is ready.
Perhaps I am missing something, but wouldn't you make your life a lot easier by doing:
require(['jQuery', 'app' ], function(jQuery, app) {
jQuery(function ($) {
app.initialize();
});
});
in your main.js?
By requiring the app from inside the domReady callback function, you should be able to require the domReady module, and then the app module synchronously.
define(['require', 'domReady'], function(require, domReady) {
domReady(function() {
require(['app'], function(app) {
app.initialize();
});
});
});
If you follow the doc at: http://requirejs.org/docs/jquery.html, you will be invited to embed a require-jquery library in the head of your document:
<script data-main="main" src="libs/require-jquery.js"></script>
However, if you look at the source for the example, made available on github, you will see that 'require-jquery.js' is generated by simple concatenation of the require lib file and the jquery lib file:
cat require.js jquery.js > ../jquery-require-sample/webapp/scripts/require-jquery.js
That means that you could replace the script embed in the head part with the following script embeds anywhere in your document (for instance at the very bottom of it).
<script src="libs/require.js"></script>
<script src="libs/jquery-1.8.0.js"></script>
<script>require(["main"]);</script>
Because the jquery lib defines itself as a module with:
define( "jquery", [], function () { return jQuery; } );
You can thereafter use jquery as a require reference in any of your script. For instance:
require(["jquery"], function($) { }
I'm continually getting index.js:7 Uncaught TypeError: Cannot read property 'View' of null, which indicates that Backbone is not loaded/present, however, when I review the loaded resources on the page backbone-min.js is present.
Since there are no 404 errors, I believe the issue is with the script itself. Does anyone see any issues with the script below?
NOTE: For convenience I uploaded my code here. The zip file contains all the relevant js files. If you scroll to the bottom of the webpage, you should see a "slow
download" button, once you click it you'll be prompted to enter a
captcha code. After entering the code, the actual download button
(under the "slow download" button) will appear within a few seconds.
View: index.js
define([
"jQuery",
"Underscore",
"Backbone"
// I've tried using the modules above as well as direct loading using order! as seen in the following lines.
//"order!libs/jquery/jquery-min",
//"order!libs/underscore/underscore-min",
//"order!libs/backbone/backbone-min",
],
function($, _, Backbone){
console.log(_) // prints "undefined"
console.log(Backbone) // prints Object
var IndexView = Backbone.View.extend({ // At this line I now get: Uncaught TypeError: Cannot call method 'extend' of undefined
render: function(){
$(this.el).html("<h1>Welcome Dan!</h1>");
$("body").html(this.el);
}
});
return new IndexView();
});
So the key to this issue is changes in underscore.js. Specifically the fact it now supports AMD (Asynchronous Module Definition). The fact that underscore no longer attaches itself to the global namespace when require is detected is breaking the scheme used to allow a standard asynchronous require syntax but still maintain synchronous loading.
Now that JQuery, Underscore & Backbone (0.5.3 does not register itself, you need a this) support async loading, you are able to abandon those hacked libraries in favor of the standard ones and require the names those libraries register themselves with. Like this:
Main.js
require.config({
baseUrl: "js",
paths: {
jquery: "libs/jquery/jquery",
underscore: "libs/underscore/underscore",
backbone: "libs/backbone/backbone"
},
waitSeconds: 10
});
require([
"app"
],
function(App){
App.initialize();
console.log("Main initialized...");
});
index.js
define([
"jquery",
"underscore",
"backbone"
],
function($, _, Backbone){
console.log(_);
console.log(Backbone);
var IndexView = Backbone.View.extend({
render: function(){
var username = getCookie("username");
var data = {username: username};
var compiled = _.template("<h1>Welcome <%= username %></h1>", data);
$(this.el).html(compiled);
$("#lt-col").html(this.el);
}
});
return new IndexView();
});
Other define's were changed to reflect the new lowercase alias's.
Pull the fixed code here
Even though Backbone 0.5.3 registers itself as an AMD module, it doesn't return anything. (Same thing for underscore) If you change your line:
function($, _, Backbone){
To
function($){
It will work. For a more requirejs-ish solution, create a module for backbone that looks like:
define(
[
'order!libraries/underscore',
'order!libraries/backbone.0.5.3'
],
function () {
return Backbone;
}
);
--UPDATE-- additional info
<head>
<title>Index2</title>
<script src="../../scripts/libraries/require.js" type="text/javascript"></script>
<script type="text/javascript"">
require({
baseUrl: 'scripts'
}, [
'order!libraries/jquery',
'order!libraries/underscore',
'order!libraries/backbone.0.5.3'
], function ($) {
console.log(Backbone);
});
</script>
</head>
I'm trying to load Backbone and Underscore (as well as jQuery) with RequireJS. With the latest versions of Backbone and Underscore, it seems kind of tricky. For one, Underscore automatically registers itself as a module, but Backbone assumes Underscore is available globally. I should also note that Backbone doesn't seem to register itself as a module which makes it kind of inconsistent with the other libs. This is the best main.js I could come up with that works:
require(
{
paths: {
'backbone': 'libs/backbone/backbone-require',
'templates': '../templates'
}
},
[
// jQuery registers itself as a module.
'http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js',
// Underscore registers itself as a module.
'http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.1/underscore-min.js'
], function() {
// These nested require() calls are just due to how Backbone is built. Underscore basically says if require()
// is available then it will automatically register an "underscore" module, but it won't register underscore
// as a global "_". However, Backbone expects Underscore to be a global variable. To make this work, we require
// the Underscore module after it's been defined from within Underscore and set it as a global variable for
// Backbone's sake. Hopefully Backbone will soon be able to use the Underscore module directly instead of
// assuming it's global.
require(['underscore'], function(_) {
window._ = _;
});
require([
'order!http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js',
'order!app'
], function(a, app) {
app.initialize();
})
});
I should mention that, while it works, the optimizer chokes on it. I receive the following:
Tracing dependencies for: main
js: "/home/httpd/aahardy/requirejs/r.js", line 7619: exception from uncaught JavaScript throw: Error: Error: Error evaluating module "undefined" at location "/home/httpd/aahardy/phoenix/trunk/ui/js/../../ui-build/js/underscore.js":
JavaException: java.io.FileNotFoundException: /home/httpd/aahardy/phoenix/trunk/ui/js/../../ui-build/js/underscore.js (No such file or directory)
fileName:/home/httpd/aahardy/phoenix/trunk/ui/js/../../ui-build/js/underscore.js
lineNumber: undefined
http://requirejs.org/docs/errors.html#defineerror
In module tree:
main
Is there a better way of handling this? Thanks!
RequireJS 2.X now organically addresses non-AMD modules such as Backbone & Underscore much better, using the new shim configuration.
The shim configuration is simple to use: (1) one states the dependencies (deps), if any, (which may be from the paths configuration, or may be valid paths themselves). (2) (optionally) specify the global variable name from the file you're shimming, which should be exported to your module functions that require it. (If you don't specify the exports, then you'll need to just use the global, as nothing will get passed into your require/define functions.)
Here is a simple example usage of shim to load Backbone. It also adds an export for underscore, even though it doesn't have any dependencies.
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
}
}
});
//the "main" function to bootstrap your code
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone) { // or, you could use these deps in a separate module using define
});
Note: this simplified code assumes that jquery, backbone and underscore are in files named "jquery.js", "backbone.js" and "underscore.js" in the same directory as this "main" code (which becomes the baseURL for require). If this isn't the case, you'll need to use a paths config.
I personally think with the built-in shim functionality, the advantages of not using a forked version of Backbone & Underscore outweigh the benefits of using the AMD fork recommended in the other popular answer, but either way works.
Update: As of version 1.3.0 Underscore removed AMD (RequireJS) support.
You can use the amdjs/Backbone 0.9.1 and the amdjs/Underscore 1.3.1 fork with AMD support from James Burke (the maintainer of RequireJS).
More info about AMD support for Underscore and Backbone.
// main.js using RequireJS 1.0.7
require.config({
paths: {
'jquery': 'libs/jquery/1.7.1/jquery',
'underscore': 'libs/underscore/1.3.1-amdjs/underscore', // AMD support
'backbone': 'libs/backbone/0.9.1-amdjs/backbone', // AMD support
'templates': '../templates'
}
});
require([
'domReady', // optional, using RequireJS domReady plugin
'app'
], function(domReady, app){
domReady(function () {
app.initialize();
});
});
The modules are properly registered and there is no need for the order plugin:
// app.js
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
return {
initialize: function(){
// you can use $, _ or Backbone here
}
};
});
Underscore is actually optional, because Backbone now gets its dependencies on its own:
// app.js
define(['jquery', 'backbone'], function($, Backbone){
return {
initialize: function(){
// you can use $ and Backbone here with
// dependencies loaded i.e. Underscore
}
};
});
With some AMD sugar you could also write it like this:
define(function(require) {
var Backbone = require('backbone'),
$ = require('jquery');
return {
initialize: function(){
// you can use $ and Backbone here with
// dependencies loaded i.e. Underscore
}
};
});
Regarding the optimizer error: doublecheck your build configuration. I assume your path configuration is off. If you have a directory setup similar to the RequireJS Docs you can use:
// app.build.js
({
appDir: "../",
baseUrl: "js",
dir: "../../ui-build",
paths: {
'jquery': 'libs/jquery/1.7.1/jquery',
'underscore': 'libs/underscore/1.3.1-amdjs/underscore',
'backbone': 'libs/backbone/0.9.1-amdjs/backbone',
'templates': '../templates'
},
modules: [
{
name: "main"
}
]
})
For reference, as of version 1.1.1 (~Feb '13), Backbone also registers itself as an AMD module. It will work with requirejs without the need to use its shim config. (James Burke's amdjs fork also hasn't been updated since 1.1.0)
Good news, Underscore 1.6.0 now supports requirejs define !!!
versions below this require shims, or requiring underscore.js then blindly hoping that the "_" global variable hasn;t been smashed (which to be fair is a fair bet)
simply load it in by
requirejs.config({
paths: {
"underscore": "PATH/underscore-1.6.0.min",
}
});
I will write down directly, you can read the explaination on requirejs.org, you could use below code as a snippet for your everyday use; (p.s. i use yeoman) (since many things updated, im posting this as of Feb 2014.)
Make sure you included script in your index.html
<!-- build:js({app,.tmp}) scripts/main.js -->
<script data-main="scripts/main" src="bower_components/requirejs/require.js"></script>
<!-- endbuild -->
Then, in main.js
require.config({
shim: {
'backbone': {
deps: ['../bower_components/underscore/underscore.js', 'jquery'],
exports: 'Backbone'
}
},
paths: {
jquery: '../bower_components/jquery/jquery',
backbone: '../bower_components/backbone/backbone'
}
});
require(['views/app'], function(AppView){
new AppView();
});
app.js
/**
* App View
*/
define(['backbone', 'router'], function(Backbone, MainRouter) {
var AppView = Backbone.View.extend({
el: 'body',
initialize: function() {
App.Router = new MainRouter();
Backbone.history.start();
}
});
return AppView;
});
I hope I was useful.!
require.config({
waitSeconds: 500,
paths: {
jquery: "libs/jquery/jquery",
jqueryCookie: "libs/jquery/jquery.cookie",
.....
},
shim: {
jqxcore: {
export: "$",
deps: ["jquery"]
},
jqxbuttons: {
export: "$",
deps: ["jquery", "jqxcore"]
}
............
}
});
require([
<i> // Load our app module and pass it to our definition function</i>
"app"
], function(App) {
// The "app" dependency is passed in as "App"
// Again, the other dependencies passed in are not "AMD" therefore don't pass a parameter to this function
App.initialize();
});