BackboneJS + Requirejs: Backbone is null issue - backbone.js

In the router.js. The firebug console alerts Backbone is null there. Why???
app.js
define([
'order!jQuery',
'order!Underscore',
'order!Backbone',
'order!router' // Request router.js
],
function($, _, Backbone, Router){
App = {
initialize: function() {
console.log("app.js initalize");
Router.initialize();
}
};
return App;
});
router.js
define([
'order!Underscore',
'order!Backbone'
],
function(_, Backbone){
var AppRouter = Backbone.Router.extend({
// Console shows Backbone is null here, why?
// I'm sure the config is correct.
routes: {
'*actions': "defaultAction"
},
defaultAction: function(actions){
// We have no matching route, lets just log what the URL was
console.log('No route:', actions);
}
});
var initialize = function(){
console.log("Router initialize");
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});

Backbone does not support the AMD and it doesn't register as module. When required it registers normally as a global Backbone object, also since 1.3 Underscore doesn't support AMD neither and if you will require Backbone and Underscore under Backbone and _ namespaces they will overwrite its values in this modules scope to undefined cause of that.
jQuery supports AMD but it also registers itself as a global instance. Basically it means that you don't need to require jquery, underscore and backbone multiple times - it's enough if you do it once in your requirejs main script

The alternative is to hack the backbone.js library.
Note: This will allow you to reference Backbone.js and underscore.js library within your require.js defines, but it will not stop them from being added to the global namespace/window object. This requires a little more hacking.
Find:
(function(){var l=this,y=
Replace it with:
define('backbone',['underscore','jquery'],function(_,$){
var l = this;
(function(){var y=
Add this to the bottom of the page:
return l.Backbone;
});
Then do the same for underscore.js
Prefix the beginning with:
define('underscore',function(){
Add to the bottom of the page:
return this._;
});

Related

Where to put onload code in RequireJs and Backbone App

Doing my first backbone app and I'm using a structure somewhat like this tutorial
I'm wondering where the correct place for me to put my onload code, such as setting up onclick listeners etc would be?
I have:
A simple Bootstrap
require.config({
paths: {
jquery: 'libs/jquery/jquery',
underscore: 'libs/underscore/underscore',
backbone: 'libs/backbone/backbone'
}
});
require([
// Load our app module and pass it to our definition function
'app',
], function(App){
// The "app" dependency is passed in as "App"
App.initialize();
});
The App.js
define(['routers/search'], function(router){
var initialize = function(){
this.router = new router();
}
return { initialize: initialize};
});
And then a simple router that calls the relevenent function in the router also defined as a module that calls the relevent function on the router depending on the page.
My feeling is that this function in the router is where I should be putting my onload code. Is that correct?
One possibility is to use the RequireJS domReady plugin (it's available for download from their short plugins list): http://requirejs.org/docs/api.html#pageload
Here's the example they give:
require(['domReady'], function (domReady) {
domReady(function () {
//This function is called once the DOM is ready.
//It will be safe to query the DOM and manipulate
//DOM nodes in this function.
});
});
So then you can just incorporate it into your normal RequireJS structure, knowing that both the DOM is loaded plus any additional dependencies you might have listed alongside it.

How to use jasmine-require with Backbone?

I would like to use my require.js for Backbone within a Jasmine test suite; and I am working on the setup.
I've seen the use of https://github.com/scottburch/jasmine-require in this test setup: https://github.com/Patternslib/Patterns/blob/master/tests/index.html
Now, I would like to inject Backbone into the game:
describe("Basic view test", function() {
var view;
requireDependencies(["underscore", "backbone"], function(_, Backbone) {
view = Backbone.View.extend({el: "li" });
});
it("has el property", function() {
expect(view.el).toBe("li");
});
});
But my Backbone setup is not loaded correctly, see screenshot:
What's missing?
My current setup is here: https://github.com/mulderp/backbone-require-test/tree/master/spec
The problem here was the load orders of the dependencies in the global scope that is not seen in the question.
Make sure underscore is loaded before backbone.

Requirejs2: How to handle own files?

I have configured requirejs to load the core libs (jquery, underscore, backbone).
Now I would like to add my backbone models, controllers, views, etc to be loaded asyncronly
I found a lots of tutorials to this topic and lots of "ready" boilerplates unfortunatly I mentioned that most approaches are depreceated or rather complicated (even there are better approaches).
One example is how I configured requirejs for the main libs:
https://stackoverflow.com/a/10914666/1309847
So how do I load Backbone Views, Models, Collections, Routers, Controllers and Templates with a simple and valid Requirejs configuration?
I followed youre advice but get some strange error
main.js
require.config({
paths: {
jquery: 'vendors/jquery/jquery',
underscore: 'vendors/underscore/underscore',
backbone: 'vendors/backbone/backbone'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require(['app'], function(app){
});
app.js
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone){
var Message = new Backbone.Model.extend({
//idAttribute: '_id',
//defaults: { body: '' }
//url: function(){ return this.id ? '/messages/' + this.id : '/messages'; }
});
var newMessage = new Message({ body: 'hi' });
newMessage.save();
});
The error occours in app.js:
Uncaught TypeError: Object [object Object] has no method 'apply'
When I comment the new Backbone.Model.extend part I don't get any error anymore.
in my experience, the best way to bootstrap your application is by creating a Backbone.Router. So you can associate urls with your application functionality.
If you are using RequireJS+Backbone, you probably have a main.js where RequireJS is configured (paths, shims, etc). The first call to "require" is used to load a initial script in order to bootstrap the whole app.
For example:
/**
* main.js - RequireJS bootstrap
*/
require.config({
paths: {
//your paths
},
shim: {
//your shims
}
});
require(
[
'app' //app.js is at the same directory as main.js
],
function(app) {
app.init();
}
);
then in app.js you can create a new Router instance, or you can just start creating Views and Models.
For further reference: http://addyosmani.github.com/backbone-fundamentals/
So as I have now understood right: You have to wrap a requirejs function around youre own custom js file.
The function is called define. The first parameter is an array of the dependencies which you have defined in the main.js file or a relative path to another custom js from you.
The second parameter is the callback which holds the original file. Important is that you return the object, function, array or variable which you want to share.
The whole thing looks like this:
define(
['underscore', 'backbone'], // the dependencies (either relative paths or shortcuts defined in main.js
function(_, Backbone){ // the return statement of the deps mapped to a var
var MessageModel = Backbone.Model.extend({ // the original code, file
defaults: { body: '' },
initialize: function(){}
});
return MessageModel; // the return statement, sharing the "final result", sometimes you return the initialize parameter
});
The same for a collection wrapping the models:
define(
['jquery', 'underscore', 'backbone', 'models/message_model'], // deps and the last one is the relative path
function($, _, Backbone,MessageModel){ // same as above explained
var MessageCollection = Backbone.Collection.extend({
model: MessageModel,
initialize: function(){}
});
return MessageCollection;
});
I now only have to figure out how I can bootstrap to whole application. But I think I need more knowledge of backbone to do this :)

How should I bootstrap my web app?

I am looking for the best way to bootstrap my web app using Backbone.Marionette, Backbone.Router and Requirejs.
The following implementation works but I would like to know if this is the right way to make things.
Here's some of my code (*).
My questions are:
1) Is right the following data flow (index.html -> conf.js -> router.js -> app.js) ?
2) The Backbone.View for each region (header, sidebar .....) should be instantiate in router.js or app.js or booths according the context?
// index.html
<!doctype html>
<html lang="en">
<head>
<!-- Load the script "/js/conf.js" as our entry point -->
<script data-main="js/conf" src="js/vendor/require.js"></script>
</head>
<body>
</body>
// js/config.js
require.config({
// some code
});
require(['app']); // instead of require(['conf']);
// router.js
define([
'app',
// others modules
],
function(App, $, _, Backbone, Marionette){
"use strict";
var AppRouter = Backbone.Marionette.AppRouter.extend({
routes: {
test: test,
"*defaults": "home"
}
var initialize = function ()
{
var app_router = new AppRouter;
};
return {
initialize: initialize
};
});
// js/app.js
define(
[
// some modules
],
function ($, _, Backbone, Router, Mustache, Layout, SidebarView) {
var MyApp = new Backbone.Marionette.Application();
MyApp.addInitializer(function () {
$('body').html(Layout);
MyApp.addRegions({
header: '#header',
sidebar: '#sidebar',
mainColumn: '#main-column',
rightColumn: '#right-column'
});
});
MyApp.addInitializer(function () {
var sidebarView = new SidebarView();
MyApp.sidebar.show(sidebarView);
});
MyApp.on("initialize:after", function () {
// Router.initialize();
});
MyApp.start();
return MyApp;
});
This looks pretty good overall. There are a few things I might change, but these are mostly personal preferences:
1) Invert the relationship between the router and app files, and use an initializer to start the router.
Right now you have a circular dependency between the router and the app files, and that's never a good thing. Even though RequireJS can handle that fine, it's a bad idea in many other ways as it can lead to code that doesn't quite work the way you expect.
2) In your router file, set up an initializer that instantiates the router.
3) Don't start backbone.history from the router file.
It's common, and suggested, to have multiple routers in a project. But you can only call Backbone.History.start() once. Start that in the app.js file, using the "after:initialize" event of the router
MyApp.on("after:initialize", function(){ Backbone.History.start(); }
4) Extract your initializer callbacks in to functions that are called from a single initializer
While there's nothing technically wrong with using multiple initializers - and you will need multiple initializers across multiple modules - I suggest using a single initializer within a single module, and have that one initializer call other functions defined in your module.
5) Call addRegions outside of initializers
There's no guarantee that your initializers will run in the order you add them. It depends on how the individual browser handles things.
For example, your app.js file could look like this:
// js/app.js
define(
[
// some modules
],
function ($, _, Backbone, Router, Mustache, Layout, SidebarView) {
var MyApp = new Backbone.Marionette.Application();
MyApp.addRegions({
header: '#header',
sidebar: '#sidebar',
mainColumn: '#main-column',
rightColumn: '#right-column'
});
MyApp.addInitializer(function(){
showLayout();
initSidebar();
});
MyApp.on("initialize:after", function(){
Backbone.History.start();
});
function initSidebar() {
var sidebarView = new SidebarView();
MyApp.sidebar.show(sidebarView);
}
function showLayout() {
$('body').html(Layout);
}
MyApp.start();
return MyApp;
});
...
alright, that looks like a lot more changes than I originally thought. :) But like I said, your set up looks fine over all. These are things that I would do, but are not necessarily requirements to make your app work.

RequireJS loads scripts before the view is initialized

I have a Backbone view as a requirejs module. The problem is that requirejs load the http://localhost/remote/script/here.js before the view is even initialized. Is it because the script isn't inside a requirejs module?
define([
'jquery',
'undescore',
'backbone',
'http://localhost/remote/script/here'
], function($, _, Backbone, Luajs){
var View = Backbone.View.extend({
initialize : function(options) {
},
render : function() {
this.$el.html('<p>my view</p>')
return this;
}
});
return View;
});
The array you have as the first argument to define is the depedencies of your view. So yes it is loaded and parsed before the View.
Also note that unless you use modified versions of backbone and underscore, they ar not AMD compliant. You will need to wrap them with a plugin to load them properly.
you try to define the view Backbone after the load the module.
You can do this, in the define () method of RequireJS. The array of this function contains parameters that defines module dependencies.

Resources