I would like to use mixins with my BackBone Views.
Here is my mixin:
var mixin = {
events: {
"click" : "doStuff"
},
doStuff: function() { alert("bah!"); }
}
Here is how I mix it into two separate views:
var view1 = Backbone.View.Extend({ ... });
_.extend(view1.prototype, mixin);
var view2 = Backbone.View.Extend({ ... });
_.extend(view2.prototype, mixin);
The trouble I am running into is that the click event only seems to work in view1. If I initialize view2 first, then the click event only works in view2.
Any ideas what I am doing wrong?
Thanks (in advance) for your help.
You can override the extend method on backbone to due the sort of inheritance and merging you expect. You just need to dig through the documentation a little bit and the objects to find what you want.
This way you have a BaseObject then extend off of that your two objects.
Related
I've been diving into the scary stuff recently.. :) scary stuff being the source of popular js frameworks like backbone.js, angular.js, vue.js and so on.
I'll take Backbone as an example here. I am trying to figure out how is a model attached to the view?
Here is the code and if someone could just point out the part where this is happening, would be awesome!
https://github.com/jashkenas/backbone/blob/master/backbone.js
Actually, the part I don't understand is that there is not innerHTML called anywhere, so how is the element being populated with the data?
Backbone is not Angular, it doesn't bind model to html for you, also it does not actually renders views for you, you have to implement render methods in your views and call them when you find appropriate. In fact, I think it might be confusing to developer coming from 2-way binding frameworks. Backbone is all about giving all the control to the developer, but you have to do all the work yourself.
Minimal model => view flow example would be something like
var MyModel = Backbone.Model.extend({
defaults: {
test: 'test',
},
initialize: function (options) {
console.log(this.get('test'));
},
});
var MyView = Backbone.View.extend({
el: '#your-el',
initialize: function (options) {
this.template = _.template('<div><%= test %></div>');
this.listenTo(this.model, 'change', this.render);
},
render: function () {
// rendering happens here, innerHTML, $.html(), whichever you prefer
// and you can pass model data as this.model.toJSON()
// or you can pass an object with data
// also, you probably will need a templating library or use
// bundled underscore _.template(html, data) method to render blocks of html
// for example, with underscore
this.$el.html(this.template(this.model.toJSON()));
return this; // for chaining
},
});
var myModel = new MyModel();
var myView = new MyView({
model: myModel,
});
myModel.set('test', 'newValue'); // view should render after this call
Check the list of built-in events at backbonejs.org.
I'm trying to find best option to make Backbone views reusable. I goggled and found many different solutions but not sure which one suits my requirements. Basically I'm going to have many widgets filled with real time data and I need a base component that will handle service subscriptions
Is following best solution for this problem:
App.View.Base = Backbone.View.extend({
baseMethod: function( params ) {};
});
App.ExtendedView.Base = App.View.Base.extend({
// new stuff here
// overriding App.View.Base.baseMethod
baseMethod: function( params ) {
// overriding stuff here
App.View.Base.prototype.baseMethod.call(this, params); // calling super.baseMethod()
}
});
Is there any better approach? or should I use mixins?
I might be inclined to favour composition over inheritance here, and create a spinner view, and use instances of it in other views that require spinner functionality.
More info: Prefer composition over inheritance?
The typical rule-of-thumb I use for stuff like this is if there are any immutable methods in the base class that provide a common context for all your sub-classes, then inheritance makes sense. For instance, I've created a BaseView class for my Backbone application that looks something like this:
define(function() {
return Backbone.View.extend({
/**
* show() and hide() are immutable
*/
show : function() {
this.beforeShow();
this.doShow();
this.afterShow();
},
hide : function() {
this.beforeHide();
this.doHide();
this.afterHide();
},
doShow : function() {
this.$el.show();
this.trigger('displayComplete', {action : 'show'});
},
doHide : function() {
this.$el.hide();
},
//Override the following to extend behavior of the view
//before and/or after the view is shown/hidden.
beforeShow : function() {},
beforeHide : function() {},
afterShow : function() {},
afterHide : function() {}
});
});
This is a pretty simple example, but it has proven to make things much easier for development of my application, as my central controller object is given a common interface for showing and hiding views. I suppose you could use composition here as well, but that requires doing an explicit extend() at runtime. You get the same result in either case, but I just prefer to have the functionality available when I instantiate my views.
Another thought is that it really depends upon what you want to accomplish. Inheritance is much more rigid than composition, but again, it depends upon what you ultimately want to accomplish, and sometimes enforcing rigidity to maintain a context is a good thing.
Is there a handy way to throw/catch (custom) events from/to multiple levels of child/parent views in Backbone?
Let me explain my situation. I'm catching "keydown" events and check if some interactions should been done on the parent level. If not, then I'm calling the childView function for that. My BaseView looks something like this:
var BaseView = Backbone.View.extend({
keydown: function(e){
return this;
},
});
It works fine until I'm not trying to throw other customEvents to interact with childViews of childViews. Which by the way, don't know about the existence of themselves.
I can't just simple do something like the stuff below, cause my parent don't even know all childViews of the subView. I'm trying to do something like this:
eg. Blur the subChildViews input
Parent.subview.subsubview.trigger('blurInput');
I'm really sure I'm on the wrong way with my event-pushing-"keydown"-method,
could someone point me the right direction?
EDIT:
The raw BackboneJS isn't really build for something like that, but there is a Module out there. MarionetteJS was my solution, it provides everything I was looking for. Subviews, modular logic and an optimized cross view event system.
There is an awesome getting started tutorial on smashingmagazine.com
Well there's no handy solution in backbone form scratch, cause nesting views is not handeled there is any way.
What i can advise you is to set the parental relation(so each view knows it's parent) rather then parent knows all of it's subviews, and as long as view is an event emitter by default toy can do something like this [pseudo-code]:
parent view:
var BaseView = Backbone.View.extend({
keydown: function(e){
this.trigger("custom.keydown");
return this;
},
});
child view:
var ChildView = Backbone.View.extend({
initialize:function(){
this.parent.on("custom.keydown",this.keydown,this);
}
});
I started to use mixins from this post: Proper way of doing view mixins in Backbone
var MyMixin = {
foo: "bar",
sayFoo: function(){alert(this.foo);}
}
var MyView = Backbone.View.extend({
// ...
});
_.extend(MyView.prototype, MyMixin);
myView = new MyView();
myView.sayFoo(); //=> "bar"
Which works fine unless its a conflicting method in the mixing: Example render() in mixin and render() in the actual view.
How can I go about firing the mixin methods first then the actual view methods?
I might recommend using Backbone.Cocktail which provides a really succinct way of specifying mixins (that respect inheritance):
var Mixin = {
initialize: function() {
console.log("I'll be called as well as the class's constructor!");
}
};
var View = Backbone.View.extend({
mixins: [ MyMixin ]
});
I've detailed it in this blog post.
Why don't you simply use the other answer provided on that question: Backbone-Mixin Gist?
I got this code:
(function($){
var ListView = Backbone.View.extend({
el: $('body'), // el attaches to existing element
events: Where DOM events are bound to View methods. Backbone doesn't have a separate controller to handle such bindings; it all happens in a View.
events: {
'click button#add': 'addItem'
},
initialize: function(){
_.bindAll(this, 'render', 'addItem'); // every function that uses 'this' as the current object should be in here
this.counter = 0; // total number of items added thus far
this.render();
},
render() now introduces a button to add a new list item.
render: function(){
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<ul></ul>");
},
addItem(): Custom function called via click event above.
addItem: function(){
this.counter++;
$('ul', this.el).append("<li>hello world"+this.counter+"</li>");
}
});
var listView = new ListView();
})(jQuery);
from this tutorial.
I understand that Backbone.js introduces a MVC pattern to the front end.
But in the code above I can't see that.
Can anyone explain that to me?
There is technically no controller in backbone.js. The main structures are Models, Views, Collections (that act as arrays and contain lots of models), and Routers.
The link you listed - http://arturadib.com/hello-backbonejs/ - is probably the best way to learn Backbone.js - especially with little background in Javascript. So you are on the right track. That project is a direct lead-in to the backbone.js ToDo list tutorial: http://documentcloud.github.com/backbone/docs/todos.html
This site will also explain things at a more basic level - I found it very helpful: http://backbonetutorials.com/
That's just the view part code. See other .js files in the same tutorial. Better check out all the files from 1.js to 5.js
Better check it from first: Hello Backbone
Note that Backbone View isn't the one you expected in MVC its more like a controller or the presenter in MVP. Here is a nice article that describes this differences.