how does BackboneJS view's function binding works? - javascript

I am reading through BackboneJS View .
SearchView = Backbone.View.extend({
initialize: function(){
alert("Alerts suck.");
}
});
// The initialize function is always called when instantiating a Backbone View.
// Consider it the constructor of the class.
var search_view = new SearchView();
Is every function inside a View object called on instantiation or is it only the initialize function alone??
Is initialize more like a callback function on success of instantiating a view? what exactly is it meant for?
I went through google. But found most results having buzz words that i couldn't understand. can someone put it straight away simple? assuming I have no knowledge about underscorejs?

Only the initialize function is called on instantiation. You can regard it as a constructor of sorts.
Even in the documentation, the title of the initialize function is constructor/initialize.
... If the view defines an initialize function, it will be called when the view is first created.
It would make no sense at all if every function was called on instantiation. Imagine a case where you have some destructive logic in one of the functions of your class (which is very likely), you wouldn't want that function called right away.
Any other functions that you want to execute the moment the object is instantiated can simply be called from within the initialize function.
initialize: function(){
// alert("Alerts are not too cool (no offence).");
console.log( "Consoles are cool" );
another_init_func();
more_init_stuff();
be_awesome();
...
}

Related

Knockout subscribe scope

Is there any possibility to change the scope of the subscribe in Knockout?
I have something like this:
element =
{
type: ko.observable()
name: ko.observable()
content: ko.observable()
}
element.type.subscribe(this._typeChanged.bind(element))
Basically I want to have an access to the object which property I am subscribed to. Binding like in my code does nto work since it binds to the whole VeiwModel and not the object.
Maybe the knockout handle that when you subscribe an observable you can pass 2 parameters the first is the callback and the second is the scope/context, try something like this:
element.type.subscribe(this._typeChanged, element)
The subscribe function accepts three parameters: callback is the function that is called whenever the notification happens, target (optional) defines the value of this in the callback function, and event (optional; default is "change") is the name of the event to receive notification for.
Ref. http://knockoutjs.com/documentation/observables.html
The problem is the way in which you're creating your view model. The view model shuld be self-contained, including the functions that operate on it. It should be something like this:
var ViewModel = function() {
var self = this;
self.type = ko.observable();
self.name = ko.observable();
self.content = ko.observable();
self.type.subscribe(function(newVal) {
// here you have access to all the viewmodel properties through self
});
return self;
};
This is a constructor using the var self=this; pattern.To use the view model you need to instantiate it, i.e. var vm = new ViewModel(). (You can omit the new).
Of course, you can also define a function, and bind it to self, or receive a callback in the constructor, and bind it to self. In that case, the function implementation will have the view model accesible via this, and not self, which will be undefined inside the function body.
var doSomethignWithVm = function(newVal) {
// acces viewmodel via this
// you can also use newVal
};
You modify the constructor to receive this as a callback:
var ViewModel = function(doSomethingCallback) {
self.type.subscribe(callback.bind(self));
};
This pattern doesn't make much sense because your callback is supposed to have knowledge of your view model. In that case it makes more sense to include the subscription functionality directly inside the model.
EDIT
Note: as I've mentioned in a comment to Joel Ramos Michaliszen's answer, both of this codes are equivalent:
self.type.subscribe(callback.bind(self));
self.type.subscribe(callback.bind, self);
You can check that by seeing the source code of subscribable in knockout's gitbhub, in the file knockout/src/subscribales/subscribable.js. If you look for subscribe implementation you'll see this:
subscribe: function (callback, callbackTarget, event) {
// ...
boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
I.e. if you provide a second argument, it's used tob bind the function passed in the firt argument to it.
Although I get that I may have the wrong approach top this I am also in a stage where I will not be able to do any breaking changes to the app.
I figured out that I could use lodash to help me with this.
I ended up using partial function to append the element as a parameter in the subscribe callback:
element.type.subscribe(_.partial(this.typeChanged, element))
or in coffeescript
element.type.subscribe $_.partial #typeChanged, element
Now the chartTypeChanged has 2 parameters on the input instead of one.

Seeking explanation of structure (Revealing Module Pattern)

RE: The Odin Project: Missile Command canvas game exercise.
This project has very few submissions (it's either very new or very difficult), and the two submissions available for reference are from people possessing more experience than I. In the more accessible submission is the following:
var missileCommand = (function() {
//---------
//hundreds of lines of game logic
//including 'initialize' and 'setupListeners' methods
//---------
return {
initialize: initialize,
setupListeners: setupListeners
};
})();
$( document ).ready( function() {
missileCommand.initialize();
missileCommand.setupListeners();
});
What is the purpose/meaning/structure behind the return and the subsequent method calls within the $(function)? I'm not sure what's going on, save that removing either set breaks everything.
I know the title isn't very descriptive, but I don't have enough information to do much about it. Will edit when I know what to call it.
There's two completely separate patterns here.
var missileCommand = (function() {
//---------
//hundreds of lines of game logic
//including 'initialize' and 'setupListeners' methods
//---------
return {
initialize: initialize,
setupListeners: setupListeners
};
})();
This is an implementation of the revealing module pattern. It allows you to create a module (missileCommand) which exposes a number of public methods (here, initialize, and setupListeners). It also allows you to define and store private methods and variables; which will be defined within the area you've commented.
$( document ).ready( function() {
missileCommand.initialize();
missileCommand.setupListeners();
});
This is a jQuery ready block. When the DOMContentLoaded event has fired, jQuery will run the function provided. Here, that function calls missileCommand.initialize(), then missileCommand.setupListeners().
More often, you'll see the ready block used to attach event handlers to elements;
$(document).ready(function () {
$('#some-selector').on('click', function () {
alert('Hello');
});
});
Note that both of my explanations are a rather short introduction to each subject. Both links (and further Google-ing of the terms), will give you much more detailed explanations.
$ is a jQuery object. What it does here is it binds an anonymous function (containing the two function calls, initialize() and setupListeners()) to the event "ready", which is fired when the document (DOM) is fully loaded.
you can translate that in jQueryFind(myElement).whenCompletelyLoaded(myFunctionToExecuteOnEvent()) (this is not functional code)
The return statement within the missileCommand function makes sure than when you do var myCommands = missileCommand() it returns an object containing both the values of the initialize function and the setupListeners function.
In those 100's of lines of code you would for sure find 2 function declarations matching: function initialize(){} and function setupListeners().
The object returned simply exposes those 2 functions by returning them to allow calling them as:
missileCommand.initialize();
missileCommand.setupListeners();
The properties of the returned object could have different names but author wanted the properties to match the function names....the values of the properties are simply references to the function object declared previously

Extending Backbone functions while keeping base functionality

I'm attempting to add a bit of processing functionality to Backbone.View in the initialize function that I want to be carried over to all my Backbone Views. The problem is, I'm using Marionette so I can't do something like this:
var BaseView = Backbone.View.extend({})
because Marionette extends Backbone.View itself. Here's what I would like to do:
// Add processoring logic to an extended version of Backbone.
Backbone.View.extend({
initialize: function(options){
if(options.hasOwnProperty("vents") {
// process vents
}
// native code. Calling the library's actual original function to maintain original functionality.
Backbone.View.initialize(this, aurguments);
}
})
var CollectionView = new Marionette.CollectionView({
vents: {VentCallName: function(){}}
// When initialize is called, it'll see the vents and deal with them automatically.
});
I'm just not sure how to add the functionality to Backbone.View while maintaining whatever function logic is already in there.
EDIT
How do I actually get the initial extended functionality into Backbone.View.initialize without making a new extended instance and basing all my views off that? I can't get Marionette to use that extended view, so the extra processing has to go into Backbone.View's initialize function.
If I do this, it loops back on itself:
Backbone.View.prototype.initialize = function(){
console.log("moooo");
// custom logic then run Backbone.View.initialize native code.
Backbone.View.prototype.initialize.apply(this, arguments);
}
Backbone.View.prototype.initialize.apply(this, arguments);
Edit
Okay, well that's a slightly different question.
var oldInitialize = Backbone.View.prototype.initialize;
Backbone.View.prototype.initialize = function(){
console.log("moooo");
// custom logic then run Backbone.View.initialize native code.
oldInitialize.apply(this, arguments);
}

How do you override bindings up a JS constructor chain?

I'm trying to define a base class in JavaScript that performs a lot of common functionality upon creation. Part of that functionality is to create a component and register callback handlers to that component.
The problem I'm having is how to override the function that's being used for that callback handler in child classes that extend my base class.
In concrete terms I have a BasicPage component that creates a Table component. The BasicPage also defines a default onRowClick function that gets registered with the newly created Table.
Now I want to create a PageWithSections component that extends BasicPage (via a call(..) statement) and overrides onRowClick. The problem is the registration of the click handler with the table happens within the constructor of the base class. At the time of that registration, onRowClick hasn't been overridden yet, so the reference is to the base classes version of onRowClick.
I've made a jsBin that illustrates the problem.
http://jsbin.com/uDEjuzeQ/9/edit
If you click on each box once, in order, I want the message box display to be:
No messages yet;
row clicked;
row clicked; BasicPage onRowClicked;
row clicked; PageWithSections onRowClicked
What is the proper way to override a function up the constructor chain and bind the overridden function to something during construction of a base object?
UPDATE
This question original referenced a prototype chain, but in truth the prototypes are not actually being used in this example.
The question was updated to reflect that. This ends up being more of a question about late binding.
The biggest issue I see is that your _onRowClicked (the callback you pass into the Table) is not actually defined in a prototype anywhere.
You are not actually using prototypical inheritance - you are defining the methods inside the constructor, and calling one constructor from another.
Try refactoring your code such that some of the default behaviour for BasicPage is defined in BasicPage.prototype (which is currently not referenced/altered at all). At that point, a solution that uses prototypical inheritance might suddenly become obvious. :)
Here is some code that should work:
var BasicPage = function(){
this.name="BasicPage";
document.body.onclick=this._getClick(this);
};
BasicPage.prototype._getClick=function(me){
return function(e){
console.log("target is:,",e.target);
console.log("this is:",me.name);
}
};
var PageWithSections = function(){
//initialise parent and it's instance members
BasicPage.call(this);
//override name
this.name="PageWithSections";
};
PageWithSections.prototype=Object.create(BasicPage.prototype);
PageWithSections.prototype.constructor=PageWithSections;
var sect = new PageWithSections();
document.body.click();
The following code demonstrates how you could extend the onclick handler without copy and pasting the BasicPage code you already have:
var BasicPage = function(){
this.name="BasicPage";
document.body.onclick=this._getClick(this);
};
BasicPage.prototype._getClick=function(me){
return function(e){
console.log("re used code from BasicPage");
console.log("target is:,",e.target);
console.log("this is:",me.name);
}
};
var PageWithSections = function(){
//initialise parent and it's instance members
BasicPage.call(this);
//override name
this.name="PageWithSections";
};
//set prototype chain
PageWithSections.prototype=Object.create(BasicPage.prototype);
PageWithSections.prototype.constructor=PageWithSections;
//extend _getClick
PageWithSections.prototype._getClick=function(me){
var fn=BasicPage.prototype._getClick.call(me,me);
return function(e){
//do BasicPage click code
fn(e);
//extended code
console.log("with a little extra from PageWithSections");
};
};
var sect = new PageWithSections();
document.body.click();
More info on prototype and constructor functions here. The introduction should be very helpful and if you have time I would suggest reading all to get a good understanding of JS prototype.
My co-worker came up with one possible solution. As #cloudfeet said, it's not prototypal, but it works.
Basically he set the binding to a different instance function that in turn, called the _onRowClick function which at the time of execution would have been overridden.
http://jsbin.com/uDEjuzeQ/16/edit

Getting a reference to a model in an event callback

I'm not sure if I'm doing this right, first time playing with Backbone.js.
I have two views with two models and I want to use the event aggregator method to fire events between the two.
The aggregator declaration:
Backbone.View.prototype.eventAggregator = _.extend({}, Backbone.Events);
So in one view I have a line like this that will fire the removeRow method.
this.eventAggregator.trigger("removeRow", this.row);
In another view
MyView = Backbone.View.extend({
initialize: function() {
this.eventAggregator.bind("removeRow", this.removeRow);
this.model.get("rows").each(function(row) {
// Do stuff
});
},
removeRow: function(row) {
// row is passed in fine
// this.model is undefined
this.model.get("rows").remove(row);
}
});
I think I understand why this.model is undefined, but what can I do to maintain a reference so that I can use this.model in the callback? I thought about passing the model to the first view and then passing it back in the trigger call, but that seems to make the entire point of an event aggregator pointless. If I have the model I can just call the .remove method directly and have lost the benefit of my first view being unaware of the model. Any suggestions?
I think you have binding problem.
You have two ways to assure that this will be the View instance:
1. Using bindAll
In your View.initialize() you can add this line:
_.bindAll( this, "removeRow" )
Interesting post of #DerickBailey about this matter
2. Using the optional third argument in your bind declaration
Like this:
this.eventAggregator.bind("removeRow", this.removeRow, this);
Backbone documentation about this matter
Supply your View object as third parameter of the bind method:
this.eventAggregator.bind("removeRow", this.removeRow, this);
The third parameter is the context of calling your callback. See the docs.
Also, you can use .on() instead of .bind() which is shorter...
You need to bind this so scope isn't lost. The blog link on the other answer uses underscore's bindAll
initialize: function() {
_.bindAll(this, 'removeRow');
this.eventAggregator.bind("removeRow", this.removeRow);
this.model.get("rows").each(function(row) {
// Do stuff
});
},

Categories

Resources