How to organise common code - javascript

Just getting started with backbone.js, and one of the things I've noticed is that many of my models, collections and views share some very similar methods. I'd like to refactor them & call them from an extracted location (/lib?). I went searching for documentation and/or examples, and was surprised by how little I found (specifically, none). So, a few questions:
Is there a reason I'm overlooking as to why there are so few examples of backbone methods factored out into common libraries?
Is there a standard/agreed upon location in backbone projects for shared code?
Any backbone classes and/or common plugins to help store common methods?
Any ideas appreciated - thanks in advance.
(EDIT) Example added:
Take this code from a view. (Admittedly it's too short be actually worth refactoring, but its simplicity makes it a concise example)
destroy: () ->
#model.destroy()
#remove()
return false
Suppose I wanted to refactor it into:
destroy: () ->
restful_destroy_method(this)
which then called:
restful_destroy_method: (view) ->
view.model.destroy()
view.remove()
return false
from a common library. Any reason why nobody else seems to do this?

It depends on the situation, and what your common code is.
In the case of your example, what I might do would be to create a more specific View to extend from.
Apologies for the straight JavaScript, I'm not as fluent in CoffeeScript to use it in an answer.
DestroyableView = Backbone.View.extend({
destroy: function () {
this.model.destroy();
this.remove();
return false;
}
});
Then, instead of creating new Backbone.View()s, I'd create new DestroyableView()s. DestroyableView could have other common functions, or you could create several different parent definitions and use _.extend() to apply them all to a single object.

You can use a "Basic View" which own the generic methods :
// Create a Basic View which have all generic methods
var BasicView = Backbone.View.extend({
restful_destroy_method: function () {
this.model.destroy();
this.remove();
return false
}
});
// Create a view which herits the methods of BasicView
var ExampleView = BasicView.extend({
destroy: function () {
this.restful_destroy_method();
}
});
You can show an example on jsFiddle here : http://jsfiddle.net/Atinux/YDMNg/

Related

In Backbone, how do I have an after_render() on all views?

I am maintaining a javascript application and I would like there to be a jquery function invoked on pretty much every view. It would go something like this:
SomeView = Backbone.Marionette.ItemView.extend
initialize: ->
#on( 'render', #after_render )
after_render: ->
this.$el.fadeOut().fadeIn()
Clearly there is a better way to do this than have an after_render() in each view? What is the better way to do it? If you can give an answer that includes jasmine tests, I'll <3 you ;)
The event you are looking for is onDomRefresh. See here for the documentation:
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.view.md#view-domrefresh--ondomrefresh-event
Create your own base view class and put your afterRender code in it. When you create a view, inherit from this class.
var MyApp.ItemView = Backbone.Marionette.ItemView.extend({
afterRender: function() {
// This will be called after rendering every inheriting view.
}
});
var SpecificItemView = MyApp.ItemView.extend({
// this view will automatically inherit the afterRender code.
});
In general, it seems to be considered good practice to define your own base views for all 3 view types. It will enable you to easily add global functionality later.
There is a common pattern used across all Backbone frameworks, normally they have a render method which in turn calls beforeRender, renderTemplate and afterRender methods.
render:function(){
this.beforeRender();
this.renderTemplate();// method names are just indicative
this.afterRender();
return this;
}
In your Base view you can have these methods to be empty functions, and implement them wherever you want it. Not sure this answer applies to Marionette
Combining thibaut's and Robert Levy's answer, the correct solution would be:
var baseView = Backbone.Marionette.ItemView.extend({
onDomRefresh: function() {
// This will be triggered after the view has been rendered, has been shown in the DOM via a Marionette.Region, and has been re-rendered
// if you want to manipulate the dom element of the view, access it via this.$el or this.$('#some-child-selector')
}
});
var SpecificItemView = baseView.extend({
// this view will automatically inherit the onDomRefresh code.
});

Backbone structure for custom objects

Looking at some backbone examples, I see some simple models like this:
var Vehicle = Backbone.Model.extend(
{
summary: function () {
return 'Vehicles move';
}
});
or
Vehicle = (function () {
return Backbone.Model.extend({
defaults: {
},
initialize: {
}
});
})();
Edit: (clarification)
I was wondering if someone could explain the differences between the two ways of defining backbone objects and what's more conventional. I know they don't have the same methods inside, but I'm more interested in how in the first one, they extend the backbone model, and the second one, they wrap it in a closure. I'm not sure if I really grasp what's going on in each and when you would use which pattern. Thanks in advance!
I would consider the first form much more conventional, especially since I don't even see the second form on the main Backbone.js website at all.
To understand how they do the same thing, first notice that Backbone.Model.extend() is a function that also returns a function:
> Backbone.Model.extend()
function () { return parent.apply(this, arguments); }
So the variable Vehicle ends up being set to a function that is a model constructor method either way you look at it. I would consider the second form more indirect and unnecessarily complex, though: it is setting Vehicle to the result of calling a function that, itself, just returns Backbone.Model.extend(), so its just a more convoluted way of saying the same thing.
If all the properties for the model are easy to define, pattern 1 is suggested. However, if any property is complex to implement thus need a "private" helper function which you do not want expose it either in your model or in global object, better to utilize the closure to hide it. that is the pattern 2.
An Example:
Vehicle = (function () {
function helper1() {} //don't want to expose it
function helper2() {}
return Backbone.Model.extend({
defaults: {
},
initialize: {
}
summary: function() {
helper1();
helper1();
}
});
})();

Backbone.js: Should .render() and .remove() be able to reverse each other?

Because Backbone.js is pretty flexible, I am wondering about the best approach for certain things. Here I'm wondering if I'm supposed to build my application's views so that '.render()' and '.remove()' correctly reverse each other.
At first, the way that seems cleanest is to pass the view a ID or jQuery element to attach to. If things are done this way though, calling '.render()' will not correctly replace the view in the DOM, since the main element is never put back in the DOM:
App.ChromeView = Backbone.View.extend({
render: function() {
// Instantiate some "sub" views to handle the responsibilities of
// their respective elements.
this.sidebar = new App.SidebarView({ el: this.$(".sidebar") });
this.menu = new App.NavigationView({ el: this.$("nav") });
}
});
$(function() {
App.chrome = new App.ChromeView({ el: $("#chrome") });
});
It seems preferable to me to set it up so that .remove() and .render() are exact opposites:
App.ChromeView = Backbone.View.extend({
render: function() {
this.$el.appendTo('body');
this.sidebar = new App.SidebarView({ el: this.$(".sidebar") });
this.menu = new App.NavigationView({ el: this.$("nav") });
}
});
$(function() {
App.chrome = new App.ChromeView();
});
What does the Backbone.js community say? Should .remove() and .render() be opposite sides of the same coin?
I prefer that render does NOT attach the view's element to the dom. I think this promotes loose coupling, high cohesion, view re-use, and facilitates unit testing. I leave attaching the rendered element to a container up to either the router or a main "layout" type container view.
The nice thing about remove is that it works without the view having knowledge of the parent element, and thus is still loosely coupled and reusable. I definitely don't like to put random DOM selectors from my layout HTML (#main or whatever) into my views. Definitely bad coupling there.
I will note that in certain annoying situations, some things like the chosen jQuery plugin require some code to run AFTER the element has been attached to the DOM. For these cases I usually implement a postAttach() callback in the view and try to keep the amount of code there as small as possible.
Yes, the in-house View.remove() is very agressive.
For the propose of re-create the View again using an external el I am used to rewrite it like this:
remove: function(){
this.$el.empty();
return this;
}
But I don't think the framework should implement magic behavior to avoid this external DOM elements deletion.
This framework behavior is aggressive, ok, but it is very cheap to customize it when needed as we see above.
What about this? If we just have .initialize and .render take a parentSelector property, we can do this and end up with a usage that is:
Loosely coupled
Reversable .remove()/.render()
Single method
instantiation & rendering for the calling method
eg:
// Bootstrap file
curl(['views/app']).then(
function(App){
app = new App('body');
});
// view/app.js
define([
'text!views/app.tmpl.html'
, 'link!views/app.css'
]
, function (template) {
var App
// Set up the Application View
App = Backbone.View.extend({
// Standard Backbone Methods
initialize: function (parentSel) {
console.log('App view initialized')
if (parentSel) { this.render(parentSel) }
}
, render: function (parentSel) {
if (parentSel) { this._sel = parentSel } // change the selector if render is called with one
if (!this._sel) { return this } // exit if no selector is set
this.$el.appendTo(this._sel)
this.$el.html(
this.compiledTemplate({ 'content':'test content' })
);
return this
}
// Custom Properties
, compiledTemplate: _.template(template)
})
return App
});
// External usage
// I can call .remove and .render all day long now:
app.remove()
app.render()
app.remove()

When initializing a backbone view

How do I make it so that a function runs every time a backbone.js view is initialized?
I'm looking for something that I can put on outside of my normal view code, as an extension to backbone.js.
The idea is to reduce the amount of boilerplate.
Since Javascript is not a true object oriented programing language, you can't use inheritance to solve your problem as you could if it was java or c#.
One possible solution is to use the factory design pattern.
Instead of instantiating your view directly, you can call a factory method that will instantiate your view.
var viewFactory = function(view, viewOptions) {
//perform your boilerplate code
return new view(viewOptions);
}
AView = Backbone.View.extend({});
var person = new Backbone.Model({name: 'Paul'});
var view = viewFactory(AView, { model: person });
Here's a jsfiddle example
It's not as an elegant solution that is possible with other languages, but it does the job.
use the builtin backbone.js initialize function:
http://documentcloud.github.com/backbone/#View-constructor
var ItemView = Backbone.View.extend({
initialize: function(){
alert('View Initialized');
}
});
EDIT: I should be more clear.
In the words of Patrick Ewing found here http://podcast.rubyonrails.org/programs/1/episodes/railsconf-2007:
"if it walks like a duck and talks like a duck, it’s a duck, right? So if this duck is not giving you the noise that you want, you’ve got to just punch that duck until it returns what you expect"
Duck Punch (or Monkey Patch if you prefer) the Backbone object.
Backbone.View.prototype.initialize = function(){
alert('I overrode the default initialize function!');
}
You can use Backbone.Events.
On the top level of your app or on the global object:
app.eventManager = {};
_.extend(app.eventManager, Backbone.Events);
app.eventManager.bind("newView", app.yourfunction(view));
And in the initialize method of any view you want to trigger your function:
app.eventManager.trigger("newView", this);
where "this" is the view instance passed as the "view" parameter to your function.

Passing in functions to Backbone.View.extend

Recently I'm having an argument with some co-workers about something that I
find incorrect.
We're using Backbone in a large application and my way to create views is
the 'standard' backbone way :
var MyView = Backbone.View.extend({
className: 'foo',
initialize: function() {
_.bindAll(this, 'render' /* ... more stuff */);
},
render: function() {
/* ... render, usually
using _.template and passing
in this.model.toJSON()... */
return this;
}
});
But someone in the team recently decided to do it this way :
var MyView = Backbone.View.extend( (function() {
/* 'private stuff' */
function bindMethods(view) {
_.bindAll(view, /* ... more stuff */);
};
function render(view) {
/* ... render, usually
using _.template and passing
in view.model.toJSON()... */
};
return {
className: 'foo',
initialize: function() {
bindMethods(this);
render(this);
}
};
}());
That's the idea in pseudo-code .
Having read the BB source and read tutorials, articles I find this to be a
bad practice (for me it makes no sense), but I'd love some feedback from
other Backbone developers/users
Thanks in advance
One benefit I see from using the closure is providing a private scope for variables and functions that you don't want to be accessible from code outside the view.
Even so, I haven't seen many Backbone apps use a closure to define a view/model/collection etc.
Here's an email from Jeremy Ashkenas concerning this issue as well.
Yes, using closures to create instances of objects with private variables is possible in JavaScript. But it's a bad practice, and should be avoided. This has nothing to do with Backbone in particular; it's the nature of OOP in JavaScript.
If you use the closure pattern (also known as the "module" pattern), you're creating a new copy of each function for each instance you create. This completely ignores prototypes, and is terribly inefficient both in terms of speed and especially in terms of memory use. If you make 10,000 models, you'll also have 10,000 copies of each member function. With prototypes (with Backbone.Model.extend), you'll only have a single copy of each member function, even if there are 10,000 instances of the class.
I totally agree with Paul here. Sometimes you may find it necessary to define methods and properties that are private and can't be messed with from outside. I think it depends wether you need this scoping mechanism in your class or not. Mixing both approaches, with respect to the requirements you have for the class wouldn't be so bad, would it?

Categories