I've been developing Backbone applications for a little while now, and am just starting to learn to use Backbone with Require.js.
In my backbone app that I am refactoring, I defined a namespace like this: App.model.repo. This model is used over and over again in different views. I do the same thing with a few collections, for example, App.collection.files. These models and collections are bootstrapped in with the initial index file request.
I did find this example, which looks like a great way to get that bootstrapped data in. However, I am struggling with the best way to reuse/share these models and collection between views.
I can think of three possible solutions. Which is best and why? Or is there another solution I am missing entirely?
Solution 1
Define these common modules and collections in the index (when they are bootstrapped in), and then pass them along to each Backbone view as an option (of initialize).
define(['jquery', 'underscore', 'backbone', 'handlebars', 'text!templates/NavBar.html'],
function($, _, Backbone, Handlebars, template){
return Backbone.View.extend({
template: Handlebars.compile(template),
initialize: function(options){
this.repoModel = options.repoModel; // common model passed in
}
});
}
);
These seems clean as far as separation, but could get funky quick, with tons of things being passed all over the place.
Solution 2
Define a globals module, and add commonly used models and collections to it.
// models/Repo.js
define(['backbone'],
function(Backbone){
return Backbone.Model.extend({
idAttribute: 'repo_id'
});
}
);
// globals.js (within index.php, for bootstrapping data)
define(['underscore', 'models/Repo'],
function(_, RepoModel){
var globals = {};
globals.repoModel = new Repo(<?php echo json_encode($repo); ?>);
return globals
}
);
define(['jquery', 'underscore', 'backbone', 'handlebars', 'text!templates/NavBar.html', 'globals'],
function($, _, Backbone, Handlebars, template, globals){
var repoModel = globals.repoModel; // repoModel from globals
return Backbone.View.extend({
template: Handlebars.compile(template),
initialize: function(options){
}
});
}
);
Does this solution defeat the whole point of AMD?
Solution 3
Make some models and collections return an instance, instead of a constructor (effectively making them Singletons).
// models/repo.js
define(['backbone'],
function(Backbone){
// return instance
return new Backbone.Model.extend({
idAttribute: 'repo_id'
});
}
);
// Included in index.php for bootstrapping data
require(['jquery', 'backbone', 'models/repo', 'routers/Application'],
function($, Backbone, repoModel, ApplicationRouter){
repoModel.set(<?php echo json_encode($repo); ?>);
new ApplicationRouter({el: $('.site-container')});
Backbone.history.start();
}
);
define(['jquery', 'underscore', 'backbone', 'handlebars', 'text!templates/NavBar.html', 'models/repo'],
function($, _, Backbone, Handlebars, template, repoModel){
// repoModel has values set by index.php
return Backbone.View.extend({
template: Handlebars.compile(template),
initialize: function(options){
}
});
}
);
This I worry could get real confusing about what is a constructor and what is an instance.
End
If you read this far, you are awesome! Thanks for taking the time.
In my case, I prefer option 3. Although, to prevent confusion, I put every singleton instance in their own folder named instances. Also, I tend to separate the model/collection from the instance module.
Then, I just call them in:
define([
"instance/photos"
], function( photos ) { /* do stuff */ });
I prefer this option as every module is forced to define its dependencies (which is not the case via namespace for example). The solution 2 could do the job, but if I'm using AMD, I want my module as small as possible - plus keeping them small make it easier to unit test.
And lastly, about unit test, I can just re-define the instance inside my unit test to use mock data. So, definitely, option 3.
You can see an example of this pattern on an Open source app I'm working on ATM: https://github.com/iplanwebsites/newtab-bookmarks/tree/master/app
I would take a look at this example repo https://github.com/tbranyen/github-viewer
It is a working example of backbone boiler plate (https://github.com/tbranyen/backbone-boilerplate)
Backbone Boiler plate does a lot of unnecessary fluff, but what is really useful about it, is that it gives some clear directions on common patterns for developing complex javascript apps.
I'll try and come back later today to answer you question more specifically (if someone doesn't beat me to it :)
I prefer Solution 1. It is generally good to avoid using singletons, and using globals is also something to avoid, especially since you are using RequireJS.
Here are some advantages I can think of for Solution 1:
It makes the view code more readable. Someone looking at the module for the first time can immediately see from looking at the initialize function which models it uses. If you use globals, something might be accessed 500 lines down in the file.
It makes it easier to write unit tests for the view code. Since you could possibly pass in fake models in your tests.
Related
I'm trying to minimize server calls by avoiding any requests I can.
Let's say, for the sake of a example, I have a collection of Matchboxes which belong to Users and have Tags assigned, and then also have a collection of Tags and a collection of Users as part of other pages. Getting matchboxes retrieves the user and tag info, so that I can instantiate all required models with one request, accessing the Tags and Users pages retrieves similar collections (only they deal only with their respective models).
My problem: if matchboxes is one page, and tags and users are two other pages, what's a good way to make sure only one model is ever instantiated for any given entity, ie. if I go into users or tags and edit an entry associated with a matchbox the matchbox entry should have the same entry assigned allowing it to listen and react to the updates with out requiring sending requests when going back to the matchbox page in the example.
I've looked over Backbone.relational but it doesn't seem to do what I need, and would rather not wall myself into a framework. So solutions involving patterns are preferable.
Ended up using http://pathable.github.io/supermodel/ which uses the pattern of overwriting the model attribute on collections with a custom function which calls a special Model.create that itself returns an existing (updated with the new values if necessary) instance of said model. The Model.create call has to be used everywhere else in code for unique models.
So essentially every model has a all() method which is a collection of all instances by id. Whenever a model is added it checks it against the collection and returns an existing object if it exists; the data used to instantiate the duplicate is used to update the existing object ensuring data is not stale (which is a nice bonus to the uniqueness I wanted).
The cleanest method seems to be to just wrap the model function into a function that returns it for clearer use; then for every collection that needs to have unique models wrap said model in the function. I came up with this at the moment:
app.single = function (modelPrototype) {
return function (attrs, options) {
return modelPrototype.create(attrs, options);
};
};
(app there is just a scope global, tied to a particular namespace)
So in collections instead of,
model: app.Model
I would then use
model: app.single(app.Model),
Whenever I update a entry in one part of the application the change will trickle down to every other collection/model since if it's the same instance from the user's perspective it's the same instance in code too.
That's about all I could tell from reading the pattern though the code and documentation. Which is sufficient for my own uses.
I suspect this solution would still have some issues if you're caching renders but I haven't found a use for that (prefer to re-render whenever I can to avoid dealing with various artifacts) so it's all good for me.
Unfortunately the codebase seems to be partially abandoned, so while it works with Backbone 1.0.0 (as far as unique models go), I may need to re-create/fork the pattern in future projects.
I think you should think twice about nesting your models and collections in this way, especially if it's primarily for the purpose of easing the bootstrapping of your app. Instead, try to use id's for inter-referencing between models as much as possible. This design problem you have is most likely only the first of many to come if you structure your model/collection tree in a certain way now, only to find it too inflexible later.
That being said, If all you need is for models referencing other models/collections to be able to refer to the same model/collection instance, then simply instantiating them during bootstrap and passing them in to their respective parent models would be sufficient. You could either load some bootstrap data in one request, or preferably inline that data in the HTML:
<script>
var bs_data = {
users : [
...
],
tags : [
...
],
matchboxes : [
...
]
};
</script>
And then instantiate the corresponding models or collections using the bootstap data.
var matchboxes = new Matchboxes();
matchboxes.set(bs_data.matchboxes);
var users = new Users({matchboxes:matchboxes});
users.set(bs_data.users);
The bootstrap data would come from the same backend so your models and collections would already be in sync without having to fetch anything.
As for design patterns; passing dependencies as constructor arguments is actually the dependency injection pattern, albeit more automated solutions to do so exist.
To make sure only one model is ever instantiated, and it is shared among the other elements that use it, being able to listen and update when any of the elements make a change to it, you can use a Singleton pattern. You can read more about it here
If you use Requirejs you can get same effect if you always return the model instantiated. For example:
// the shared model
define([
'jquery',
'underscore',
'backbone'
], function ($, _, Backbone) {
'use strict';
var Model = Backbone.Model.extend({
// ...
});
// return instantiated, so we'll get the same object back whenever we use this model (singleton)
return new Model();
});
// a view using the model
define([
'jquery',
'underscore',
'backbone',
'model'
], function ($, _, Backbone, modelInstance) {
'use strict';
var View = Backbone.View.extend({
initialize: function () {
// listen to what other elements do
this.listenTo(modelInstance, 'eventFromOtherElement', this.doSomething);
// when this element does something, other elements should be listening to that event
modelInstance.trigger('thisViewEvent');
},
doSomething: function () {
// ...
}
});
return View;
});
I'm working on a project using Require.js, Backbone and Marionette, and the define/function calls at the top of my files are getting a bit ludicrous. I'd like to find a way to move the "includes" out of the topmost define block, and into the extend where the are relevant.
So my structure roughly looks like this:
define(['underscore','jquery','handlebars','someTemplate','someModel',etc...], function(_,$,Handlebars,template,model,etc...){
var someView = Backbone.Marionette.ItemView.extend({
// code here
});
return someView;
});
So as I add more views to the file, that define list gets really, really, long. I've tried doing something like:
var someView = define(['someTemplate','someModel'], function(template, model){
return Backbone.Marionette.ItemView.extend({
// code here
});
]);
But then someView is undefined when I call it later. Am I doing this wrong, or is it not possible?
You should split your modules. Having a really long list of dependencies is a sign that your module does to much. Most of the time all view need is model or collection and maybe some subViews and the global eventbus.
Also think about the need to require jQuery,Backbone etc. This are stuff that you will need in most of your modules, its easier to combine them in one file and load them in the first place. There is no much advantage to require this files, all it does is cluttering your define section with a lot of boilerplate.
The define call btw. does not return anything thats why someView is undefined in example. If you really wanna go with this solution you have to use require when you use someView later on. But even then its much cleaner to put someView in its own module file.
you can use your require.js config file dependencies shim to aggregate your "common" or needed
modules into one namespace:
shim: {
marionette: {
deps: ["backbone", "json2", "bootstrap", "backboneRpc"],
exports: 'Marionette'
},
flot_pie: {
deps: ['jquery', 'charting', 'flot_resize'],
exports: '$.plot'
},
}
so now you can call all you need like this:
define([
'marionette',
'flot_pie'
],
function(Marionette, graph) {
I'm new to backbone, but have watched several tutorial screencasts on it, both with and without requirejs.
My question involves the setup structure (both file structure if using require, and/or variable/object structure).
Most of the tutorials I have watched, seem to prefer a App.Models, App.Collections, and App.Views approach, and each item inside has the name of the module: ie,
App.Models.todo = Backbone.Model.extend({...});
App.Collections.todos = Backbone.Collection.extend({...});
App.Views.todo = Backbone.View.extend({...});
After a little research, trying to find someone that uses the same style as I would like to use, I finally found: File structure for a web app using requirejs and backbone. They seem to prefer more of a App.[Module Name] method: ie,
App.Todo.Model = Backbone.Model.extend({...});
App.Todo.Collection = Backbone.Collection.extend({...});
App.Todo.Views = Backbone.View.extend({...});
I personally prefer the App.[Module Name] structure over having my modules split up, but would like to know the benefits, if any, of having the different structures.
Which structure do you use, and how has it helped you over a different structure you may have seen or used in the past?
I like the approach described in this blog:
http://weblog.bocoup.com/organizing-your-backbone-js-application-with-modules/
If you are using requireJS you don't need/want to attach the models/views to a global namespace object attached to the window (no App.Views, App.Models). One of the nice things about using requireJS or a different AMD module loader is that you can avoid globals.
You can define a model like this:
define(['underscore', 'backbone'],
function(_, Backbone) {
var MyModel = Backbone.Model.extend({});
return MyModel;
});
Then you define a view:
define(['underscore', 'backbone', 'tpl!templates/someTemplate.html'],
function(_, Backbone, template) {
var MyView = Backbone.View.extend({});
return MyView;
});
Now you have a model and a view with no globals. Then if some other module needs to create one of these (maybe your App module), you add it to the define() array and you have it.
I'm migrating a Backbone basic app to Marionette and I would like to use ICanHaz.js as a template system (based on Mustache).
I'm using AMD and Require.js and the only way to make ICanHaz.js working with it and Backbone was to use jvashishtha's version.
I first implemented the app in pure Backbone style.
In particular I used to load each template as raw strings with the Require.js' text plugin and then add the template to the ich object. This create a method in ich object that has the same name of the loaded template:
define([
'jquery',
'underscore',
'backbone',
'iCanHaz',
'models/Job',
'text!templates/Job.html'
], function( $, _, Backbone, ich, Job, jobTemplate) {
var JobView = Backbone.View.extend({
render: function () {
/* since the render function will be called iterativetly
at the second cycle the method ich.jobRowTpl has been already defined
so ich will throw an error because I'm trying to overwrite it.
That is the meaning of the following if (SEE: https://github.com/HenrikJoreteg/ICanHaz.js/issues/44#issuecomment-4036580)
*/
if (_.isUndefined(ich.jobRowTpl)){
ich.addTemplate('jobRowTpl', jobTemplate);
};
this.el = ich.jobRowTpl(this.model.toJSON());
return this;
}
});
return JobView;
});
So far so good. The problem comes with Marionette.
The previous code works fine in the Backbone version, but there is no need to define a render function in Marionette's views.
Marionette assumes the use of UnderscoreJS templates by default. Someone else has already asked how to use Mustache with Marionette.
From that answer I've tried to implement my solution. In app.js:
app.addInitializer(function(){
//For using Marionette with ICH
var templateName=''; //not sure if this would help, anyway it makes no difference
Backbone.Marionette.Renderer.render = function(template, data){
if (_.isUndefined(ich.template)){
//this defines a new template in ich called templateName
ich.addTemplate(templateName, template);
};
return ich.templateName(data);
}
But the console throws me:
Uncaught TypeError: Object #<Object> has no method 'templateName'
So the template has not been defined. Anyway, any hint if I'm in the right direction?
I think it's just a problem with your JS inside your renderer function.
This line:
return ich.templateName(data);
Will look for a template literally called "templateName"
What you want, since templateName is a variable is something like:
return ich[templateName](data);
Then it will interprete the value of the templateName variable instead.
I'm just getting started with Backbone. I went through the first two PeepCode screencasts which were great and now I'm digging in on a quick detached (no server side) mock-up of a future app.
Here's what I'm looking to build (roughly). A series of five text boxes - lets call these Widgets. Each Widget input, when selected, will display a pane that shows Tasks associated with the Widget and allow the user to create a new Task or destroy existing Tasks.
At this point, I'm thinking I have the following models:
Widget
Task
The following collections:
Tasks
Widgets
The following views (this is where it gets hairy!)
WidgetListView
- Presents a collection of Widgets
WidgetView
- sub-view of WidgetListView to render a specific Widget
TaskPaneView
- Presented when the user selects a Widget input
TaskCreateView
- Ability to create a new Task associated with selected Widget
TaskListView
- Presents a collection of Tasks for the given widget
TaskView
- Displays Task detail - sub-view of TaskListView
Assuming that's reasonable, the trick becomes how to display a TaskPaneView when a WidgetView is selected. And futhermore, how that TaskPaneView should in turn render TaskCreateViews and TaskListViews.
The real question here is: Does one cascade render events across Views? Is it permissible for a Root view to know of sub-views and render them explicitly? Should this be event-driven?
Apologies if this is an open-ended question, just hoping someone will have seen something similar before and be able to point me in the right direction.
Thanks!
Definitely make it event driven. Also, try not to create views that are closely coupled. Loose coupling will make your code more maintainable as well as flexible.
Check out this post on the event aggregator model and backbone:
http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/
The short version is you can do this:
var vent = _.extend({}, Backbone.Events);
and use vent.trigger and vent.bind to control your app.
Pre p.s.: I have made a gist for you with the code that I wrote below:
https://gist.github.com/2863979
I agree with the pub/sub ('Observer pattern') that is suggested by the other answers. I would however also use the power of Require.js along with Backbone.
Addy Osmani has written a few GREAT! resources about Javascript design patterns and about building Backbone applications:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/
http://addyosmani.com/writing-modular-js/
http://addyosmani.github.com/backbone-fundamentals/
The cool thing about using AMD (implemented in Require.js) along with Backbone is that you solve a few problems that you'd normally have.
Normally you will define classes and store these in some sort of namespaced way, e.g.:
MyApp.controllers.Tasks = Backbone.Controller.extend({})
This is fine, as long as you define most things in one file, when you start adding more and more different files to the mix it gets less robust and you have to start paying attention to how you load in different files, controllers\Tasks.js after models\Task.js etc. You could of course compile all the files in proper order, etc, but it is far from perfect.
On top of this, the problem with the non AMD way is that you have to nest Views inside of each other more tightly. Lets say:
MyApp.classes.views.TaskList = Backbone.View.extend({
// do stuff
});
MyApp.views.App = Backbone.View.extend({
el: '#app',
initialize: function(){
_.bindAll(this, 'render');
this.task_list = new MyApp.classes.views.TaskList();
},
render: function(){
this.task_list.render();
}
});
window.app = new MyApp.views.App();
All good and well, but this can become a nightmare.
With AMD you can define a module and give it a few dependencies, if you are interested in how this works read the above links, but the above example would look like this:
// file: views/TaskList.js
define([], function(){
var TaskList = Backbone.View.extend({
//do stuff
});
return new TaskList();
});
// file: views/App.js
define(['views/TaskList'], function(TaskListView){
var App = Backbone.View.extend({
el: '#app',
initialize: function(){
_.bindAll(this, 'render');
},
render: function(){
TaskListView.render();
}
});
return new App();
});
// called in index.html
Require(['views/App'], function(AppView){
window.app = AppView;
});
Notice that in the case of views you'd return instances, I do this for collections too, but for models I'd return classes:
// file: models/Task.js
define([], function(){
var Task = Backbone.Model.extend({
//do stuff
});
return Task;
});
This may seem a bit much at first, and people may think 'wow this is overkill'. But the true power becomes clear when you have to use the same objects in many different modules, for example collections:
// file: models/Task.js
define([], function(){
var Task = Backbone.Model.extend({
//do stuff
});
return Task;
});
// file: collections/Tasks.js
define(['models/Task'], function(TaskModel){
var Tasks = Backbone.Collection.extend({
model: TaskModel
});
return new Tasks();
});
// file: views/TaskList.js
define(['collections/Tasks'], function(Tasks){
var TaskList = Backbone.View.extend({
render: function(){
_.each(Tasks.models, function(task, index){
// do something with each task
});
}
});
return new TaskList();
});
// file: views/statistics.js
define(['collections/Tasks'], function(Tasks){
var TaskStats = Backbone.View.extend({
el: document.createElement('div'),
// Note that you'd have this function in your collection normally (demo)
getStats: function(){
totals = {
all: Tasks.models.length
done: _.filter(Tasks, function(task){ return task.get('done'); });
};
return totals;
},
render: function(){
var stats = this.getStats();
// do something in a view with the stats.
}
});
return new TaskStats();
});
Note that the 'Tasks' object is exactly the same in both views, so the same models, state, etc. This is a lot nicer than having to create instances of the Tasks collection at one point and then reference it through the whole application all the time.
At least for me using Require.js with Backbone has taken away a gigantic piece of the puzzling with where to instantiate what. Using modules for this is very very helpful. I hope this is applicable to your question as well.
p.s. please also note that you'd include Backbone, Underscore and jQuery as modules to your app too, although you don't have to, you can just load them in using the normal script tags.
For something with this kind of complexity, I might recommend using Backbone Aura, which has not yet had a stable release version. Aura essentially allows you to have multiple fully self-contained Backbone apps, called "widgets," running on a single page, which might help disentangle and smooth over some of your model/view logic.
From a classical MVC perspective, your views respond to changes in their associated models.
//initialize for view
initialize : function() {
this.model.on("change", this.render(), this);
}
The idea here is that anytime a view's model is changed, it'll render itself.
Alternatively or additionally, if you change something on a view, you can trigger an event that the controller listens to. If the controller also created the other models, it can modify them in some meaningful way, then if you're listening for changes to the models the views will change as well.
Similar answer to Chris Biscardi's. Here's what I have:
You create a global var Dispatcher (doesn't have to be global as long as it can be accessed from the scope of Backbone app):
Dispatcher = _.extend({}, Backbone.Events);
Dispatcher will help you execute subscribed callbacks with events that are not particularly triggered by changes in models or collections. And cool thing is that Dispatcher can execute any function, inside or outside Backbone app.
You subscribe to events using bind() in a view or any part of the app:
Dispatcher.bind('editor_keypress', this.validate_summary);
Then in another view or part of the app you trigger new event using trigger():
Dispatcher.trigger('redactor_keypress');
The beauty of using a dispatcher is its simplicity and ability to subscribe multiple listeners (e.g. callbacks in different Backbone views) to the same event.