I have a setup of multi level backbone inheritance, but would like to call back of the previous super class. Not sure if is possible.
Scenario:
BasicView -> MediumView -> HardView
Where I would love that when HardView created, it will loops to call previous super class initialize function.
Example is here:
http://jsfiddle.net/mochatony/bwB9W/
There are no implicit references to the superclass in standard JavaScript - you have to explicitly call the supertype's methods
var Basic = Backbone.View.extend({
initialize: function(){
console.log('base');
}
});
var Medium = Basic.extend({
initialize: function() {
console.log(Basic.prototype.initialize.apply(this, arguments));
console.log('medium');
}
});
var Hard = Medium.extend({
initialize:function(){
console.log(Medium.prototype.initialize.apply(this, arguments));
console.log('hard');
}
});
var hard = new Hard();
Related
So I'm currently building a pretty large framework to be used for a lot of web projects. I'd love to get some advice or best practises to the following scenario.
I have different chapters in a web-app. Only one chapter is visible at a time.
The function createView() is called when a chapter is visited the first time. It creates the view via a handlebars template, inserts all the copy from an XML doc (we have an XML for each language), and starts the chapter via the start() function. start() is called whenever a chapter gets visited again. It's just inserting the view into the DOM and enables all the functionality like click-events, other modules (galleries, sliders, ect) and so forth.
var Chapter = function(name) {
this.name = name;
this.createView();
};
Chapter.prototype.createView = function() {
var self = this;
// get handlebars template
this.template = config.app.getTemplate('chapters/'+self.name).then(function(hbs) {
// compile hbs template, insert data here - if there is any
var html = hbs();
// save jQuery collection of HTML as view
self.view = $(html);
// insert copy from XML doc
self.view.find('[data-text]').each(function() {
var theID = '#'+$(this).attr('data-text');
var theText = config.xml.find(theID).text();
$(this).html(theText);
});
// start the experience
self.start();
});
};
Chapter.prototype.start = function() {
// stop active experience
if(config.experiences.active) {
config.experiences.active.stop();
}
// scroll to top
$(window).scrollTop(0);
// save this chapter as the active one
config.experiences.active = this;
// insert view into DOM
this.view.appendTo($('main'));
// enable functionality
this.initModules();
this.initNavigation();
this.initLocking();
};
Now, every instance of Chapter will need some custom functions as every chapter can look different across a lot of projects. But I am not sure if this is the best approach:
Use a lof of if-else in initCustoms.
Chapter.prototype.start = function() {
...
this.initCustoms();
};
Chapter.prototype.initCustoms = function() {
if(this.name === 'Fire and Ice') {
// do abc
} else if(this.name === 'Second Sons') {
// do xyz
}
};
So is adding a bunch of if-else statements really the way to go? It seems so... dirty.
I also though about:
create an object for the custom chapter and inherit its prototype
var FireAndIce = function() {
// do abc
}
FireAndIce.prototype = Chapter.prototype;
FireAndIce.prototype.constructor = Chapter;
Chapter.prototype.start = function() {
...
this.initCustoms();
};
Chapter.prototype.initCustoms = function() {
if(this.name === 'Fire and Ice') {
// inherit functionality of FireAndIce. Can't post it here as I have not been able to do this properly.
}
...
};
But that just caused problems with inheritance, changing other instances or the main Chapter prototype. I don't want to copy all the functionality of FireAndIce over to the instance and based on my research on this subject, I can't properly inheritance from a prototype (FireAndIce) to an instance of another prototype (Chapter).
Also, just out of curiousity, is the following a bad idea?
Create a custom start event via jQuery to which I could bind as many custom handlers as I want
Chapter.prototype.start = function() {
...
this.initCustoms();
this.trigger('start');
};
Chapter.prototype.initCustoms = function() {
if(this.name === 'Fire and Ice') {
this.on('start', function() {
// do abc
});
}
...
};
Leaving aside the reasons why I would do it, is there anything wrong with it? I kinda like the idea of having a start and stop event for each chapter to which I could bind additional functionality from everywhere.
Thanks in advance for advice.
A couple of options:
Just assign a function to them
It sounds as though there will only be a few Chapter instances and that each chapter is a one-off (there's only one copy of the FireAndIce chapter), for instance. In that case, you can just create the chapter instances and then assign their initCustoms function after you create them:
var fireAndIce = new Chapter();
fireAndIce.initCustoms = function() {
// stuff for Fire and Ice
};
var secondSons = new Chapter();
secondSons.initCustoms = function() {
// stuff for Second Sons
};
// ...
Use Prototypical Inheritance and Constructor Functions
But if you want to do this with inheritance, here's how that looks:
function Chapter() {
}
Chapter.prototype.setup = function() {
// ...chapter common stuff
};
function FireAndIce() {
Chapter.apply(this, arguments); // Chain to parent constructor
// ...fire and ice stuff
}
FireAndIce.prototype = Object.create(Chapter.prototype);
FireAndIce.prototype.constructor = FireAndIce
FireAndIce.prototype.initCustoms = function() {
// ...Fire and Ice custom stuff
};
function SecondSons() {
Chapter.apply(this, arguments); // Chain to parent constructor
// ...Second Sons stuff
}
SecondSons.prototype = Object.create(Chapter.prototype);
SecondSons.prototype.constructor = SecondSons
SecondSons.prototype.initCustoms = function() {
// ...Second Sons custom stuff
};
That way, FireAndIce instances share the initCustoms on their prototype, and also share everything on the Chapter prototype, because the Chapter.prototype is the prototype behind the FireAndIce.prototype object. (And similarly for SecondSons.)
Note that if the derived functions (FireAndIce, SecondSons) and Chapter have different argument lists, instead of passing all arguments to Chapter from the derived function, you can just pass what's appropriate:
Chapter.call(this, the, appropriate, args, go, here);
Use Prototypical Inheritance without Constructor Functions
Some people prefer to use prototypical inheritance without constructor functions, and therefore without using new. That's also an option:
var ChapterProto = {
setup: function() {
// ...common chapter stuff...
}
};
function createChapter() {
var rv = Object.create(ChapterProto);
// ...common chapter stuff...
return rv;
}
var FireAndIceProto = Object.create(ChapterProto);
FireAndIceProto.initCustoms = function() {
// ...Fire and Ice custom stuff...
};
function createFireAndIce() {
var rv = Object.create(FireAndIceProto);
createChapter.apply(rv, arguments);
// ...Fire and Ice stuff...
return rv;
}
var SecondSonsProto = Object.create(SecondSonsProto);
SecondSonsProto.initCustoms = function() {
// ...Second Sons custom stuff...
};
function createSecondSons() {
var rv = Object.create(SecondSonsProto);
createChapter.apply(rv, arguments);
// ...Second Sons stuff...
return rv;
}
And again, if argument lists vary, you can use .call instead of .apply.
Conclusions
For just a few objects, I would prefer just assigning functions to the instances after creating them. Less code, simpler.
But for classes of objects (in the lower-case sense), I'd use prototypical inheritance with constructor functions as above (but some others would use it without constructor functions).
The above uses Object.create, which is new with ES5 (about five years old now). If you need to support really old engines, you can polyfill the one-argument version used above (the second argument cannot be polyfilled on ES3 engines):
if (!Object.create) {
Object.create = function(proto, props) {
if (props) {
throw "The second argument to Object.create cannot be polyfilled.";
}
function ctor() {
}
ctor.prototype = proto;
return new ctor();
};
}
i was wondering how to use functions within the Backbone.View code. Can someone advise/show me how this is done properly. I understand that extending is only put into the var. I've looked at extend, prototype, super, parent, baseview and other fancy stuff. But that only confused me even more ;).
var jsHelpers = {
intVar: 300,
sum: function(a, b, callback) {
// do something interesting:
c = a + b;
d = c + intVar;
callback(c);
} //end sum function
} //end jsHelpers
/* somewhere else */
ViewThingy = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
var result = jsHelpers.sum(1, 1, function(callbackData) {
//let's do something with the return stuff:
this.$el.html(callbackData);
}); //end jsHelpers
} // end render
}); //end extend
The error is of course that the function jsHelpers.sum(); is not available in the extend.
TIA ! Vince
var View = Backbone.View.extend({
hello: function() {
console.log('hello');
},
// You can also override Backbone methods here.
initialize: function() {
// Do init code shared by all your views
}
});
// All the views in your app should extend off this view instead of Backbone.View.
var RandomView = View.extend({
initialize: function() {
// Call the parent to do init code.
View.prototype.initialize.apply(this, arguments);
this.hello();
},
// You can override methods too..
hello: function() {
// You can call the parent.
View.prototype.hello.apply(this, arguments);
}
});
Actually it is a good idea to always extend View, Model, Collection and Router when you make an app as there will always be shared functionality you want to do to not repeat the same code everywhere in your app. Typically for a view this would be stuff like the render routine such as rendering the template and rendering sub views - you wouldn't want to do that logic again in every view in your app.
Typically to share other code you would use a dependency manager like RequireJS or Browserify. But you can also have a single global object:
window.app = {};
and attach things to it:
window.app.utils = ....;
That is accessible from anywhere. Having an app object is pretty common - e.g. you would often have a Model that maintained the state of the app at app.state.
You can hook your helpers to some global namespace or make them global.
window.jsHelpers = {...}
Second way :
jsHelpers = {..} //Remove the var, it'll make jsHelpers a global variable.
I use the first one, for similar purposes.
My Backbone application has a parent class with a static property, along with two subclasses. I am attempting to modify the parent's static property from the child classes, but this does not seem to work. Here is some sample code:
var ParentView = Backbone.View.extend({}, {
staticProperty: 1,
getStaticProperty: function() {
return this.staticProperty;
},
setStaticProperty: function(value) {
this.staticProperty = value;
}
});
console.log('ParentView.staticProperty: ' + ParentView.getStaticProperty());
ParentView.setStaticProperty(2);
var ChildView1 = ParentView.extend({
initialize: function() {
console.log('ChildView1.staticProperty: ' + ChildView1.getStaticProperty());
ChildView1.setStaticProperty(3); // THIS SEEMS TO DO NOTHING
}
});
var ChildView2 = ParentView.extend({
initialize: function() {
console.log('ChildView2.staticProperty: ' + ChildView2.getStaticProperty());
}
});
var testView1 = new ChildView1();
var testView2 = new ChildView2();
Here is a jsfiddle: http://jsfiddle.net/2agTW/1/
I would expect the following output:
ParentView.staticProperty: 1
ChildView1.staticProperty: 2
ChildView2.staticProperty: 3
But instead, I get:
ParentView.staticProperty: 1
ChildView1.staticProperty: 2
ChildView2.staticProperty: 2 // I THINK THIS SHOULD BE 3
Any idea why?
You should use `ParentView' instead:
var ParentView = Backbone.View.extend({}, {
staticProperty: 1,
getStaticProperty: function() {
return ParentView.staticProperty;
},
setStaticProperty: function(value) {
ParentView.staticProperty = value;
}
});
I think Backbone's inheritance model might be a little gaaked (technical term). Or at least, it doesn't do inheritance in the classical sense. In the case of so-called "static" properties, the extend function ends up copying all properties (instance and static) to the child, so, ChildView1 and ChildView2 each have their own copies of staticProperty. When you call setStaticProperty in ChildView1, it operates in ChildView1's context, making the this operator in it's copy of the function point to ChildView1.staticProperty.
I wouldn't say the tutorial is wrong per se - this will work if you're not using inheritance, but it's definitely misleading (I suspect the author's use of CoffeeScript hides this particular problem from him). If you want all instances to reference the same properties, I'd always reference the base class and avoid the this property.
This article helped me understand Backbone's inheritance quirks - it's definitely worth a read. http://www.erichynds.com/blog/backbone-and-inheritance
I have a base class that extends a Backbone.View. I want to be able to call the super's 'initialize' after overriding the 'initialize' on a sub class. How can I accomplish calling the super of an extended class in javascript in the most robust and clearest way?
I have seen this (Super in Backbone), is there a clearer way to accomplish this without having to know who the super class is before hand?
App.Views.BaseView = Backbone.View.extend({
initialize: function(templateContext){
this.super_called = true;
}
});
For all of my sub views, I want to take advantage of the already written initialize function.
App.Views.ChildViewWorks = App.Views.extend({});
var newView = new App.Views.ChildViewWorks();
alert(newView.super_called); // print true
App.Views.ChildViewDoesNotWork = App.Views.extend({
initialize: function(templateContext){
this.super_called = false;
//what line of code can I add here to call the super initialize()?
}
});
var newViewWrong = new App.Views.ChildViewDoesNotWork();
alert(newViewWrong.super_called); //now equal to false because I have not called the super.
App.Views.ChildViewDoesNotWork = App.Views.BaseView.extend({
initialize: function(templateContext){
this.super_called = false;
App.Views.BaseView.prototype.initialize.call(this);
}
});
all my Backbone.Views are only used once in their final state. (Except item views).
Currently I handle Backbone.Views as Singleton this way:
var Singletonizer = function(Singleton) {
if (Singleton._instance) return Singleton._instance;
Singleton._instance = new Singleton();
Singletonizer(Singleton);
};
Unfortunately it isn't that nice to add this little function as dependency to each amd module in my repository.
Is there another way to handle this? Maybe overwriting the base view class?
Just have your module return a function besides your view constructor, one that returns a single instance of it instead, not unlike the following. This way when you load the module you will not automatically get an instance whether you like it or not. Instead, after loading our "FailedXhrView" module, we then get our singleton by calling FailedXhrView()
'use strict';
define(['jquery',
'underscore',
'backbone',
'text!templates/failedXhr.html'],
function($, _, Backbone, failedXhrTemplate) {
var FailedXhrView = Backbone.View.extend({
el : $('#failedxhr-modal-container'),
template : _.template(failedXhrTemplate),
render : function() {
this.$el.html(this.template({}));
this.$el.find('failedxhr-modal-containee').modal();
return this;
}
});
var instance;
return function() {
if (!instance) {
instance = new FailedXhrView();
}
return instance;
}
});
From the very recommendable book Recipes with Backbone:
From here:
// definition
MyApplication.Views.List = Backbone.View.extend();
// instantiation
$(function() {
MyApplication.ListView = new MyApplication.Views.List({
el: $('#list')
});
})
To here:
$(function() {
MyApplication.ListView = new (Backbone.View.extend({
initialize: function() {
// ...
}
}))({el: $('#list')})
});
We assign an instantiation of an anonymous class to MyApplication.ListView. In this approach, we are doing the typical extension of a top-level Backbone class with custom attributes and methods. The difference, however, is that we do not assign the result to a class name as we did earlier. Instead, we immediately create a new instance of this anonymous class. Lastly, MyApplication.ListView is assigned to the resulting object.
I have to say I've never used techniques like this. It looks to messy for me, I prefer legibility than architecture.