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.
Related
Today I was playing around with Backbone and Javascript, and came upon an interesting problem. Consider the following:
var App = {};
App.model = Backbone.Model.Extend({/* Omitted for brevity */});
App.view = Backbone.View.Extend({/* Omitted for brevity */});
App.obj = new App.view();
At this point I wanted to refactor for readability and mantainability's sake:
var App = {
model: Backbone.Model.Extend({}),
view: Backbone.View.Extend({}),
obj: new view() // Here comes trouble
}
The snippet above is what I wanted to obtain, but obviously the initialization doesn't work:
view is not in scope
App is not yet initialized, so App.view isn't usable.
this refers to window, so this.view isn't usable.
At this point, with the help of this answer I concocted a solution:
var App = {
model: Backbone.Model.Extend({}),
view: Backbone.View.Extend({}),
get obj() { delete this.obj; this.obj = new this.view(); return this.view; }
}
Using the getter I'm able to delay the creation of the object instance until used (thus after completing App initialization), and then I replace the getter from within with the object instance.
Everything works as expected, but since I'm not exactly fluent with modern JS I was wondering if there was a different or proper way to achieve what I wanted. I was also wondering if this sort of implementation could cause drawbacks or unexpected problems I'm not considering.
First and foremost I want to thank everyone for the insights. For the sake of completion, I wanted to post what seems to be the best and less obnoxious way to obtain what I wanted in the first place. While my hack works, it should never be used, and converting the literal into a constructor gives the same result with no hacks:
function App() {
this.model: Backbone.Model.Extend({});
this.view: Backbone.View.Extend({});
this.obj = new this.view();
}
var app = new App();
This version keeps itself dry, and has two added benefits: instantiation, and "private" members. It could easily be rewritten as such:
function App() {
var model: Backbone.Model.Extend({});
var view: Backbone.View.Extend({});
this.obj = new view();
}
var app = new App();
This would keep both model and view out of reach, while having easily accessible app.obj. Of course, if instantiation is an unwanted effect, nothing beats var App = {}.
Also to be noted that since my example was featuring Backbone, the constructor way is probably suggested when extensibility is welcome, and prototypes for the class can be added via _.extend(). This is the the way Backbone does it:
function App() {
// […]
}
_.extend(App.prototype, {
method1: function() {}
method2: function() {}
});
References: _.extend(), object constructors, literals vs. constructors.
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.
});
I am building an application using Durandal and I have the need to share some functionality across view models.
I have 5 screens to build and they are all virtually the same screen except that in the activate function they will call to a different api end points but otherwise the view and view models will be identical.
Is there a pattern that I should be following to structure this correctly to promote code reuse?
If the views and the view models are identical except for calling different api actions, what about just taking in a parameter as part of the route? Then in the activate function, you can switch on the parameter. The route values can be designated so that your url is relevant, like [http://site/page/subtype], where subtype is the parameter (instead of using numeric values)
Regarding inheritance, depending on the features you need, there's so many ways to do JavaScript inheritance it can be a little confusing. There are some full-featured inheritance models provided by libraries such as base2 and Prototype. John Resig also has an inheritance model that I've used successfully.
In general, I prefer to stick to simpler solutions when it comes to JS inheritance. If you need a pretty much the full set of inheritance features, those libraries are good to consider. If you only really care about accessing a set of properties and functions from a base class, you might be able to get by with just defining the view model as a function, and replacing the function's prototype with the desired base class. Refer to Mozilla's Developer Docs for good info on inheritance.
Here's a sample:
//viewModelBase
define(function (require) {
"use strict";
function _ctor() {
var baseProperty = "Hello from base";
function baseFunction() {
console.log("Hello from base function");
}
//exports
this.baseProperty = baseProperty;
this.baseFunction = baseFunction;
};
//return an instance of the view model (singleton)
return new _ctor();
});
//view model that inherits from viewModelBase
define(function (require) {
"use strict";
function _ctor() {
var property1 = "my property value";
function activate() {
//add start up logic here, and return true, false, or a promise()
return true;
}
//exports
this.activate = activate;
this.property1 = property1;
};
//set the "base"
var _base = require("viewModelBase");
_ctor.prototype = _base;
_ctor.prototype.constructor = _ctor;
//return an instance of the view model (singleton)
return new _ctor();
});
Keep in mind this example all results in what effectively is a singleton (i.e. you'll only get the same instance, no matter how many times you require() it)
If you want a transient (non-singleton) just return _ctor. Then you'll need to instantiate a new instance after you require() it.
One more note, in general, functions should be defined on the prototype, not within the constructor function itself. See this link for more information on why. Because this example results in only a single instance, it's a moot point, so the functions are inside the constructor for improved readability and also the ability to access the private vars and functions.
I want an Ember view to hold a Javascript object. Something like:
var foo = function(){
function bar(){
alert("Hi");
};
};
MyApp.MyView = Ember.View.extend({
boo: new foo();
});
MyApp.MyView.boo.bar();
I'm no really sure what your question or problem is. Perhaps you could phrase it in the form of a question.
A few tips though.
You should be careful about creating reference type values within a class definition because all instances of that class will share the same reference. In your example, every instance of MyApp.MyView will share the same boo object.
In your example, you haven't yet created a view, just build a view class. Doing: MyApp.myView = MyApp.MyView.create().append() should build you a view and insert it into the DOM.
When reference values deep in an object chain it's handy to use Ember's getPath/setPath because they bring a level of safety. Example:
if(typeof MyApp.getPath('MyView.boo.bar') === 'function') {
MyApp.getPath('MyView.boo').bar();
}
I come from a C++/Java background, but I'm having problems getting the syntax right on this javascript. This is what I was trying to accomplish. I want a base class TemplateBaseView which inherits from Backbone.View. The TemplateBaseView overrides the initialize and render function from the Backbone.View. I am also using underscore.js. Here is a brief attempts, any help would be appreciated.
function TemplateBase(){}
TemplateBase.prototype.render = function(){ ... }
TemplateBase.prototype.initialize = function(){ ... }
_.extend( TemplateBase , Backbone.View );
And I essentially want to do something like this,
var HeaderView = TemplateBase({ template: _.template($("#header_template").html())};
which would create a Backbone.View object essentially with the default render and initialize function and the template attribute specified.
Any help?
You want to use the built in extend from backbone.
var TemplateBase = Backbone.View.extend({
'this': 'is',
'a': 'class'
});
var HeaderView = new TemplateBase;
A couple things. You can refer to TemplateBase as a 'class' but there's really no such thing in javascript, that's just a useful name for people coming from class based languages. It's technically a prototype.
Next: Remember to use new when you create an instance of TemplateBase, otherwise you're just invoking a function and setting your object to whatever that function returns, rather than an instance of that prototype.
Finally, just a convention, most people would capitalize their 'classes' but not their instances. So I could change HeaderView to headerView
edit
After reading your question again, maybe you want HeaderView to be a class? In which case:
var HeaderView = TemplateView.extend({
template: _.template( $("#header_template").html())
});
var headerInstance = new HeaderView;