I've built my first backbone app and really enjoying the structure that backbone provides. However I have found myself with some minor annoyances which I am sure others have found solutions for.
So currently I have created a file structure like
models
index.js
user.js
views
index.js
user.js
So in my 'index.js' in the views folder I currently have all of the views, so lets say I have
headerView
footerView
buttonView
etc etc
So I currently have multiple 'views' inside one generic 'view js file' related to one page app. Problem is its not intuitive to find a particular view, I open a editor and find the correct view.
In other MVC, I would store each view in its own unique file, and use them as appropriate - do other users do the same here? I guess my concern is having multiple separate js files? I use the minify project to minify the js anyway, so could create a group, but wondering what others have done?
I would suggest that you take a look at require.js. Here is a tutorial that should get you started.
The core idea is sth. like the following.
each Model, View, Collection, Router, whatever resides in its own file and is called a module
at the top of each module you define its dependencies, meaning which other modules need to be loaded for your new module to work
require.js loads your dependencies and guarantees access via your self defined variables
Definiton (e.g childView.js)
define([
'jQuery',
'Underscore',
'Backbone'
], function($, _, Backbone){
return Backbone.View.extend({
//your usual view methods and properties
});
});
Reuse (e.g parentView.js)
define([
'jQuery',
'Underscore',
'Backbone',
'pathToChildView/childView.js'
], function($, _, Backbone, ChildView){
return Backbone.View.extend({
// your usual view methods and properties
// + access to your ChildView Modul
});
});
If you have trouble with the module loading syntax the sugared variant might be your friend.
It pays to separate each object into a view/template pair in hierarchical folders for better navigation. You then simply construct the child object in the view and pass the el element. This way you can reuse each object throughout the project as needed.
Related
My Backbone.View looks this way:
define(["promise!table_config", "BBModel"], function (config, myModel) {
"use strict";
return Backbone.View.extend({
initialize: function () {
this.model = new myModel({
foo: config
});
...
},
render: function () {
...
}
});
});
Is is a good or bad practice to initialise a model inside a view?
Particularly in this case, in a require.js module where 'config' is a require-promise, motivating me to put put a model inside a view.
While Backbone does have a Model and a View class, it is not a strict MVC framework (eg. it lacks a Controller class). The Backbone documentation page explains as much (emphasis mine):
How does Backbone relate to "traditional" MVC?
Different implementations of the Model-View-Controller pattern tend to disagree
about the definition of a controller. If it helps any, in Backbone,
the View class can also be thought of as a kind of controller,
dispatching events that originate from the UI, with the HTML template
serving as the true view. We call it a View because it represents a
logical chunk of UI, responsible for the contents of a single DOM
element.
Given that, even though (as #Evgenly mentioned in the comments) "its not view responsibility to instance model, its controllers task" ... since the Backbone View is (conceptually) a controller, it absolutely makes sense to create your models inside your views.
But putting that theory aside, here's a more practical answer. I work on a 3+ year-old Backbone app, along with two other developers (and more previously). In that app the vast majority of all models get created inside views (the few remaining ones get created inside routes). Not only has this not been a problem for us, but I can't even imagine any other way of doing it.
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
I'm using RequireJS 2 along with backbone and pals to build the website. The beginning of every view is usually something like this:
define([
'libs/jquery',
'libs/underscore',
'libs/backbone',
'text!templates/coletas/agendamento.html',
], function(...
My problem is that the last line expects to find the template at js/templates/coletas/agendamento.html, and so this poor programmer ends up having to put non-js files in /js/. Is there a way to avoid that and set the base url to ./templates/ everytime I use the text plugin?
I also cannot use absolute paths, because I know not where my code will end up in the server.
This snippet is from my main.js
require.config({
paths: {
loader: 'libs/backbone/loader',
jQuery: 'libs/jquery/jquery',
Underscore: 'libs/underscore/underscore',
Backbone: 'libs/backbone/backbone',
templates: '../templates'
}
});
Here is an example require.config that you can use to load in modules like my comment (sorry, I did not realize I had this in my app until I read your comment).
Once configured, then you can access various javascript libraries and your seperate templates directory like so (this snippet is from my js/views/overview/main.js:
define([
'jQuery',
'Underscore',
'Backbone',
'models/overview',
'text!templates/overview/main.html'
]
The directory structure I try to follow with requirejs is
webapp/
main.js
index.html
app.js
js/ <- your code here
libs/ <- 3rd party stuff here
templates/
styles/
etc/
...
so that the require.js -modules' dependencies are evaluated at the webapp - folder level, allowing you to maintain separation of different kinds of files.
Hope this helps!
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.