I have build and app using requirejs and use it to load html templates and a single config file via the text plugin.
After concatenation the html templates are inlined in the JS, which is fine, but I need the config.json file to still remain as a file which is loaded externally like before concatenation.
Example code of the main view
define([
'jquery',
'underscore',
'backbone',
'lib/text!templates/main.html',
'lib/text!config.json',
], function($, _, Backbone, projectListTemplate, Config) {
var MainView = Backbone.View.extend({});
})
This is my build file
({
appDir: './../public',
baseUrl: 'static/js/app',
dir: './../dist',
modules: [
{
name: 'main'
}
],
fileExclusionRegExp: /^(r|build)\.js$/,
exclude: ['config.json'],
optimizeCss: 'standard',
removeCombined: true,
paths: {
jquery: 'lib/jquery-2.0.3.min',
waypoints: 'lib/waypoints.min',
underscore: 'lib/underscore',
backbone: 'lib/backbone',
text: 'lib/text',
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery',
'waypoints'
],
exports: 'Backbone'
},
}
})
You should exclude your file in the same way you included it. Try this way:
exclude: ['lib/text!config.json']
But I just stopped using this approach, because it requires existence of config.json file in build phase. Instead I'm using nested require calls (You may use require inside your defined modules to load files in runtime). Unlike define, nested require may not be included in build process (and by default they are not) and validation phase will be fine.
Be advised, this approach has at least one disadvantage:
Nested require calls are also asynchronous. But in my case that is not a problem.
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 am having trouble understanding about requirejs.config() function.
requirejs.config({
paths: {
'text': '../lib/require/text',
'durandal':'../lib/durandal/js',
'plugins' : '../lib/durandal/js/plugins',
'transitions' : '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-3.1.0',
'bootstrap': '../lib/bootstrap/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
What does the function do? Please do not direct me to the documentation because I have read it and still found it confusing. I need a simple explanation on what this function does.
Are these scripts loaded asynchronously?
It creates aliases for script paths ant tells how to interpret bootstrap (non-AMD script) when loaded. Nothing is loaded yet. You have to require:
// we load two dependencies here
// `knockout` and `bootstrap` are expanded to values in config
// .js added to values
// callback function called when all dependencies are loaded
require(['knockout', 'bootstap'], function(Knockout, $) {
// jQuery is passed to this function as a second parameter
// as defined in shim config exports
});
The path is like declarations/definitions. So for example,
jquery: '../bower_components/jquery/dist/jquery',
you can later load this lib as follows.
define([
'jquery'
], function (jquery) {
//initialize or do something with jquery
}
You don't have to specify the absolute path of the library.
In shim, you will define dependencies. So for example,
paths: {
template: '../templates',
text: '../bower_components/requirejs-text/text',
jquery: '../bower_components/jquery/dist/jquery',
backbone: '../bower_components/backbone/backbone',
underscore: '../bower_components/underscore/underscore',
Router: 'routes/router'
},
shim: {
'backbone': ['underscore', 'jquery'],
'App': ['backbone']
}
Here backbone is dependent on underscore and jquery. So those two libs will be loaded before backbone starts loading. Similarly App will be loaded after backbone is loaded.
You might find this repo useful if you are familiar with backbone and express.
https://github.com/sohel-rana/backbone-express-boilerplate
I have two problems that are related to each other.
The first is that I want to have a folder named backbone, and that collides with having backbone as a short name for the library in the path. It seems that RequireJS thinks that backbone/model/User is a child of the backbone library or something. Solution, name the library Backbone with capital B.
The second problem is that I'm using ModelBinder, which has "backbone" as a dependency. Solution, change the minified file.
Both solutions seems like a bad hack for not doing something right in RequireJS configuration, but I cant figure out what I'm doing wrong.
My public folder structure is something like:
public/
js/
backbone/
model/
...
view/
...
vendor/
backbone.min.js
Backbone.ModelBinder.js
...
common.js
My common.js has something like this:
require.config({
baseUrl: "/js",
shim: {
'underscore': {
exports: '_'
},
"Backbone" : {
deps: ["underscore", "jquery"],
exports: 'Backbone'
}
},
paths: {
underscore: 'vendor/underscore.min',
jquery: 'vendor/jquery',
model_binder: 'vendor/Backbone.ModelBinder',
Backbone: 'vendor/backbone.min'
}
});
So my two questions are: How can I keep Backbone.ModelBinder as it is, and tell requirejs that when that library ask for backbone, its asking for js/vendor/backbone.min.js without "polluting" my global paths.
And why can't I use both backbone (lowercase b) in the path and have the directory?
One solution (yes, not so sane) is to include paths definitions for every directory under js/backbone, i.e.:
require.config({
...
paths: {
...
backbone: 'vendor/backbone.min', // NOTE lower-case 'b', as desired
"backbone/model": "backbone/model",
"backbone/view": "backbone/view",
... // and so on
}
});
This also means that there is no module directly under js/backbone.
An alternative would be to keep the capital 'B' in the module name for Backbone and use the map config for the Backbone.ModelBinder.js:
require.config({
...
paths: {
...
Backbone: 'vendor/backbone.min' // NOTE capital 'B' again
},
map: {
"model_binder": {
"backbone": "Backbone"
}
});
Thinkng retrospectively, the map can be applied to all modules so that whenever they request "backbone" (lower-case 'b') you deliver "Backbone":
require.config({
...
paths: {
...
Backbone: 'vendor/backbone.min' // NOTE capital 'B' again
},
map: {
"*": { // <------ Here difference form code above
"backbone": "Backbone"
}
});
Now anyone can ask for "backbone" and get Backbone, or "backbone/model/User" (or "backbone/module") and get the corresponding module.
Using this url:
"localhost/app2/app.html"
I would expect require.js to manipulate the paths so that all scripts it loads are prefixed with the context 'app2/'. However, I get a 404 for my app script, and when I look in firebug the browser is trying to retrieve it without the app2 context.
My file structure is:
content
|-app.html
|-main.js
|-app
|-app.js
|-main.js
|-libs
|-require.js
In app.html I have a single script tag:
<script src="libs/require.js" data-main="app/main" type="text/javascript"></script>
The main.js file has this:
require.config({
baseUrl: "/",
paths: {
xx: "app/app",
header: "app/views/header",
footer: "app/views/footer",
templates: "app/views/templates",
jquery: "libs/jquery-2.0.3",
k: "libs/kendo/kendo.core",
backbone: "libs/backbone",
marionette: "libs/backbone.marionette",
wreqr: "libs/backbone.wreqr",
babysitter: "libs/backbone.babysitter",
underscore: "libs/underscore",
text: "libs/text"
},
shim: {
jquery: {
exports: '$'
},
underscore: {
exports: '_'
},
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
},
marionette: {
deps: ['jquery', 'underscore', 'backbone'],
exports: 'Marionette'
}
}
});
require(["xx"], function (app) {
app.start();
});
And my app.js looks like this:
define(['jquery', 'underscore', 'backbone', 'marionette'],
function ($, _, Backbone, Marionette) {
var app = new Backbone.Marionette.Application();
app.addRegions({
headerRegion: "#header-region",
moduleRegion: "#module-region",
footerRegion: "#footer-region",
dialogsRegion: "#dialogs-region"
});
app.on("initialize:after", function() {
console.log("Starting App");
Backbone.history.start();
});
return app;
});
So, when I hit the url in the browser I see the app.html file is loaded first, then require.js is loaded followed by main.js. After that I get a 404 trying to load app.js from localhost/app/app.js -- the app2 context is missing.
I have tried renaming the files, in case require is getting confused (didn't work), and I have tried moving the xx path up and down in the list to see if it's a positional bug (it's not). I have tried using a path for xx starting with ./, and that doesn't work either. I have navigated to the app.js with the browser, on the expected path, and it is happily served by my http server.
At first I thought that this was a path problem, so I read the solutions here on stackoverflow, but it's not a simple path problem, because it is the context that's missing -- the root url -- not the path. AHGA.
Ok, I have the answer. I had set the baseUrl to an absolute URL, rather than relative. The correct string is:
baseUrl: './'
I use a .jst extension for template files, and load these with the requirejs text! plugin. E.g.,
define([
'jquery',
'backbone',
'underscore',
'text!templates/MyView.jst'
],
function($, Backbone, _, templateText) {
return Backbone.View.extend({
template: _.template(templateText),
initialize: function() {
},
render: function() {
}
});
});
This works swell when I test locally. However, when I try to do this after I've deployed my static files to AWS (the dynamic portions of the app run on Heroku), it fails to load the .jst files and appears to be trying to append a .js to their url's.
For reference, here's my requirejs config (from main.js)
requirejs.config({
paths: {
//directories
plugins: "lib/plugins",
//libs
jquery: "lib/jquery/1.7.1/jquery",
underscore: "lib/underscore/1.3.3/underscore",
backbone: "lib/backbone/0.9.2/backbone",
moment: "lib/moment", // date lib
//require plugins
text: "lib/require/plugins/text",
domReady: "lib/require/plugins/domReady"
},
shim: { //specify all non-AMD javascript files here.
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
underscore: {
exports: '_'
},
moment: {
exports: 'moment'
},
'plugins/jquery.colorbox': ['jquery'],
'util/jquery.dropTree':['jquery'],
'util/common':['jquery']
}
});
I just updated the text.js README with info that explains this issue. It is basically a way to use text resources across domains, but it requires a build. There is a way to override. Details here:
https://github.com/requirejs/text#xhr-restrictions