I guess I do not completely understand the way require.js works. Here is a a simple module I've created:
requirejs.config({
paths: {
'underscore' : 'libs/underscore-min',
'backbone' : 'libs/backbone-min'
}
});
define([
"underscore",
"backbone"
], function(_, Backbone) {
console.log(_);
console.log(Backbone);
var MyCollection = Backbone.Collection.extend({
initialize: function() {
this.on("all", function(event) {
console.log(event);
});
}
});
return MyCollection;
});
I load it from my html:
<script data-main="js/mycollection.js" src="js/libs/require.min.js"></script>
The problem is it works intermittently. Sometimes Backbone object is there in the function when I need it, sometimes it doesn't (and gives me http://requirejs.org/docs/errors.html#notloaded error). If I just hit reload in the browser, I'd get 50/50 change of it working.
I must be missing something really basic here, the reported error doesn't make any sense to me, I thought the whole idea of the require.js mechanism is that my function will be called only when all the dependencies are loaded.
Since Underscore and Backbone haven't been defined as AMD modules, require.js does not know that Underscore is a dependency of Backbone. So I guess what happens in 50% of your cases is that Underscore isn't loaded when Backbone tries to use it.
You can use the require.js shim config http://requirejs.org/docs/api.html#config-shim to tell require.js about the dependency structure.
My guess is that you're not using an AMD version of underscore and Backbone. If that's the case and the two packages aren't wrapped as AMD modules - then the define function which is meant for modules won't work like it should.
For non-module js scripts, the more appropriate form would be to use the require() function.
OR - you can find the AMD version of underscore and Backbone here. AMD support was taken out of Underscore and Backbone at some point.
AMD Underscore
AMD Backbone
Related
I'm developing simple AngularJS (I'm quite new with Angular) application and I encountered problem that js are loading in wrong order. Below there are relevant (I hope) snippets of my app.
index.html
(...)
<script data-main="scripts/main.js" src="scripts/require.js"></script>
(...)
main.js
requirejs.config({
baseUrl: 'scripts',
paths: {
angular: '../../bower_components/angular/angular',
jquery: 'jquery',
domReady: 'domReady',
'jquery.scrollTo': '../../bower_components/jquery.scrollTo/jquery.scrollTo',
},
shim: {
jquery: {deps: ['domReady']},
'jquery.scrollTo': {deps: ['jquery']},
angular: {deps: ['jquery.scrollTo'], exports: 'angular'}
},
packages: []
});
requirejs(['angular', 'domReady', 'jquery', 'app', 'scripts', 'jquery.scrollTo'], function(angular, domReady) {
'use strict';
domReady(function() {
angular.bootstrap(document, ['myApp']);
});
});
app.js
define('app', ['angular', 'domReady', 'jquery', 'jquery.scrollTo'], function (angular) {
return angular.module('myApp', []);
});
scripts.js
require(['domReady', 'jquery', 'jquery.scrollTo'], function(domReady, $) {
domReady(function() {
console.log('scripts.run');
});
});
I assumed that loading order should be following:
main
domReady
jquery
jquery.scrollTo
angular
scripts
app
But in real loading order is following:
The most strange for me is why scripts.js is loaded before jquery.js and jquery.scrollTo.js if require statement define that it is dependent on domReady, jquery and jquery.scrollTo?
I suggest to remove jquery completely from your code (if its possible for you) and requirejs. AngularJS has great dependency injection module which you could use to support timing of loaded parts.
Also - you could try to inline ng-app attribute inside html code insted of invoking angular via boostrap call, than you won't need "domReady".
If you can, please tell something more about your app so we can give you better response on availible solutions.
deitch's statement regarding loading order is correct. RequireJS will guarantee that the order in which the factory functions of your modules (factory == the callback you give to define) are executed will respect the dependencies specified. As far as the order in which modules are fetched the only thing you can be sure is that a module will be fetched before its factory function is executed but RequireJS otherwise has the freedom to fetch modules in any order.
In the case you are showing us, note how the modules that are first fetched after main are those which do not have a shim configuration defined for them. Modules with a shim configuration require a bit more processing from RequireJS and thus follow a different code path.
While we're at it, a few bizarre things in your code:
A path configuration that just repeats the name of the module does nothing. For instance, jquery: 'jquery'. You could remove such path specifications.
You have require at the top level of scripts.js. You should have define there instead.
Your call to define in app.js specifies a module name (1st parameter to define). Leave the module name out.
You give a jQuery a shim configuration. jQuery calls define. Giving a shim to a module that calls define is likely to result in undefined behavior. Don't do this.
ZI'm building an app where based on user preferences/configuration, I'll be loading different modules into the page.
To accomplish this, when the page loads, I run a script which builds a list of script-tags and adds all those tags to the body of the page. I also build a dependency injector list, and include the dependencies when the app is loaded.
I'm delaying app load using by putting
window.name = "NG_DEFER_BOOTSTRAP!";
and then after the script tags are added, I run
angular.element(document).ready(function () {
setTimeout(function(){
angular.resumeBootstrap();
},1000);
});
I have two problems.
First of all, for some reason, if I include angular.js in the configuration file, I get errors referring to angular not being found.
I can see that angular is being downloaded to the browser, but I'm guessing it's a timing issue, as angular takes longer to download than many of the other files.
I was going to just put angular in a regular script tag, but then I found that one of the modules I'm loading requires jQuery and jQuery UI, both to be loaded before angular, so that means more files loaded as scripts, and possibly not needed for that user.
I looked at using require.js and passing the config params to
requirejs.config.paths = //the custom built list from the users configuration file
But, from what I understand, that would also mean I'd need to wrap all of my modules in
requirejs(['jquery', 'angular', 'otherModule'],
function ($, angular, otherModule) {
// my module stuff here
});
Any other suggestions on how to accomplish this? Is there a better AMD than require.js?
I'd post all my code here, but I don't think it would help as the problem is coming from the browser downloading the files after the document is ready.
RequireJs is the best AMD implementation as far as I know.
If you want to specify your modules, you'd better to wrap your moduls in define calls:
define(['jquery', 'angular', 'otherModule'],
function ($, angular, otherModule) {
// DO NOT FORGET TO RETURN YOUR MODULE!
});
Also, why don't you start your applicaiton on domReady? You may use this requirejs plugin.
Here is a basic requirejs configuration for your case:
requirejs.config({
paths : {
jquery : 'libs/jquery',
jqueryUi : 'libs/jqueryui',
angular : 'libs/angular'
},
shim : {
angular : {exports : 'angular', deps : ['jquery']},
jqueryUI: {exports : '$', deps : ['jquery']}
},
map : {
'*' : {
domReady : 'require-plugins/domReady'
}
}
});
requirejs(['domReady!', 'angular'], function(document, angular) {
//Thank to domReady!, this callback executes on domReady events. And no need in jquery or angular:)
});
While backbone-deep-model has AMD support for use with RequireJS, it depends on an underscore mixin in an external file called underscore.mixin.deepExtend.js that is not AMD compatible.
Looking at this question: How to mixin Underscore plugins in RequireJS?, it seems that I can manually mixin deepExtend into Underscore within the shim init setting in RequireJS.
...
shim: {
...
'deep-model': {
deps: 'underscore',
init: function() {
_.mixin(/* hash of deepExtend functions */);
return _;
}
}
}
...
However, I am stuck at what to do at this point since underscore.mixin.deepExtend does not return a hash of functions that _.mixin() requires.
Is there a way to load backbone-deep-model with RequireJS without modifying source code?
This may not be the answer you're looking for, but one option that I'd personally advise is to just leave Underscore (and any mix-ins, and probably also Backbone and jQuery) out of Require entirely. The main advantage of this approach (beyond making your problem go away as a side effect) is that you don't have start every require module by importing the same library (or libraries if you do the same with Backbone/jQuery).
True, this slightly "pollutes" the global space, but in my opinion having an _ (or $ or Backbone) variable in the global namespace won't hurt anything. It will however solve your problem, avoid the need for a shim at all, and save you a lot of importing. And of course you can (and should) still use Require for your own code, as keeping your variables from "polluting" the global namespace will save you future headaches.
Just a thought.
You can shim backbone as a dependency:
shim: {
// this is an example, I don't know what are the actual dependencies are
'deep-model': ['backbone', 'underscore']
}
and them when you add it as a dependency it will operate on backbone:
define(['backbone', 'deep-model'], function(Backbone) {
// backbone now has deepModel in it
});
I have a view that requires requires Backbone, Undescore, jquery etc.
example
define(['jquery','undescore','backbone','subviewA', 'subviewB'], function($,_,Backbone, SubviewA, SubviewB){
var View = Backbone.View.extend({
//other methods here
render : function() {
this.subviewA = new SubviewA();
this.subviewA.render();
this.subviewB = new SubviewB();
this.subviewB.render();
return this;
}
});
});
subview example
define(['jquery','undescore','backbone','text!templates/subviewA'], function($,_,Backbone, template){
var SubviewA = Backbone.View.extend({
//other methods here
render : function() {
this.$el.html(template);
return this;
}
});
});
My question is if I need to include jquery, undescore and backbone in the subviews too ir I can omit them?
EDIT
I am asking cause in r.js I need every time to tell it to not to biuld those dependencies inside each module.
Theoretically, if you don't use the $ or _ symbols in your views, you don't need to list jquery and underscore as direct dependencies of your module (whether it is a view or a subview doesn't change that).
You do need to include backbone though, since you reference it directly with : Backbone.View. If you want to be absolutely sure that the Backbone symbol is defined you should declare it as a dependency.
Some libs register themselves both as AMD modules and as global variables (typically jquery does that). Backbone doesn't support AMD directly and registers itself at the global level regardless of how it is used. In theory you could not declare it as a dependency, but then you have the risk that require will try and load the script before backbone is loaded in which case the Backbone symbol will not be defined.
It doesn't matter much if you redondantly declare the dependencies except for the additional caracters, thus the additional script size.
You can omit any requirements which aren't used.
In your examples (ignoring omitted code!), you can remove jquery and undescore (sic) but not backbone (since you use it via Backbone.View.extend).
Obviously you need to keep your requirement names and variables in sync.
I am unsure how I use namespaces in an modularized (RequireJs) Backbone environment.
I have thought a bit how it could look like but am totally unsure if this is the right way.
app.js (getting executed by main.js)
define('App', ['underscore', 'backbone', 'Router'], function( _, Backbone, Router){
function initialize(){
var app = {}; // app is the global namespace variable, every module exists in app
app.router = new Router(); // router gets registered
Backbone.history.start();
}
return { initialize: initialize }
});
messages.js
define('MessageModel', ['underscore', 'backbone', 'App'], function(_, Backbone, App){
App.Message.Model; // registering the Message namespace with the Model class
App.Message.Model = Backbone.Model.extend({
// the backbone stuff
});
return App;
});
Is this the right approach or am I fully on the wrong way (if yes please correct me!)
Have a look at the TODO Backbone + requireJs example:
https://github.com/addyosmani/todomvc
Found an real example app using namespaces like mentioned in the start post: https://github.com/nrabinowitz/gapvis
Just have to test it the next days
I'm new to backbone, but just read a snippet from the requirejs docs.
A module is different from a traditional script file in that it defines a well-scoped object that avoids polluting the global namespace. It can explicitly list its dependencies and get a handle on those dependencies without needing to refer to global objects, but instead receive the dependencies as arguments to the function that defines the module. Modules in RequireJS are an extension of the Module Pattern, with the benefit of not needing globals to refer to other modules.
To me, this sounds as if when using requirejs, you can forget all about namespaces, as requirejs takes care of it. You would just have access it differently. When you want to access it from another module, you'd put a path to the file in your array of dependencies, and and feed a respective variable to the following function.
define(["folder/a-script", "folder/another-script"], function(AScript, AnotherScript) {
// In here the scripts are loaded and can be used, but are called by
// their relative name in the anonymous function.
}
);
Anyway, perhaps in some cases there is still a need to namespace something, but I think in general, it's worth reading the docs to see if you need to.