I have a code like this in JavaScript:
var addModalView = Backbone.View.extend({
tagName:"div",
el:$("#addemployee"),
showbutton:$("#addemployee_button"),
showbutton_click:function(e) {
this.$el.modal("show"); // this.$el is undefined and not working
},
initialize:function() {
this.showbutton.on("click", this.showbutton_click);
this.$el.modal("show"); // this.$el is defined and working
}
});
myaddModalView = new addModalView();
Why is this.$el defined and working on initialize but not on other key index (showbutton_click)?
The proper implementation should be like this using the events hash.
var addModalView = Backbone.View.extend({
el: $("#addemployee"), // should be avoided if possible
events: {
'click #addemployee_button': 'showbutton_click'
},
initialize: function() {
this.$el.modal("show");
},
showbutton_click: function(e) {
this.$el.modal("show");
},
});
myaddModalView = new addModalView();
If for some reason #addemployee_button is not inside "#addemployee", then the event binding should happen in whichever view actually contains it.
I already solved the problem all i need is to bind the backbone object on initialize.
var addModalView = Backbone.View.extend({
tagName:"div",
el:$("#addemployee"),
showbutton:$("#addemployee_button"),
showbutton_click:function(e) {
this.$el.modal("show"); // this.$el is now defined and working
},
initialize:function() {
this.showbutton.on("click", this.showbutton_click);
_.bindAll(this, "showbutton_click"); // bind it first on showbutton_click
this.$el.modal("show"); // this.$el is defined and working
}
});
myaddModalView = new addModalView();
This bind code is the solution and should be added on initialize: _.bindAll(this, "showbutton_click"); so you can call the backbone object inside your custom function variables using the this keyword.
Related
I'm kind of new to backbone and have a question. It seems like when an event in my view is fired, I lose the context to "this". How can I preserve this or get the original "this" for the model. Here is an example:
var MyListView = MyDBView.extend({
initialize: function(options){
},
render: function() {
//stuff here. I can access this.options here
},
dialogResponseYes: function(e){
//try to get this.options and options is undefined as this has changed to another element (a button)
}
});
So, how do I get the original context of this?
if you are using events object to bind events to your view like here http://backbonejs.org/#View-delegateEvents everything should be ok, otherwise u can hardly bind them to your view using _.bindAll
initialize: function(options){
_.bindAll(this, "dialogResponseYes");
},
or
var MyListView = MyDBView.extend({
initialize: function(options){
},
events: {
'click div': 'dialogResponseYes' //example
},
render: function() {
},
dialogResponseYes: function(e){
}
});
I'm having trouble using Marionette's CompositeView. I render my model in my CompositeView using a template and want to add a click event to it. Somehow I can't get the events to work using the events: { "click": "function" } handler on the CompositeView... What am I doing wrong?
var FactsMenuItem = Backbone.Marionette.ItemView.extend({
template: tmpl['factsmenuitem'],
initialize: function() {
console.log('factsmenuitem');
},
onRender: function() {
console.log('factsmenuitem');
}
});
var FactsMenuView = Backbone.Marionette.CompositeView.extend({
template: tmpl['factsmenu'],
itemView: FactsMenuItem,
itemViewContainer: ".subs",
events: {
'click': 'blaat'
},
blaat: function() {
console.log('this is not working');
},
initialize: function() {
this.model.get('pages').on('sync', function () {
this.collection = this.model.get('pages');
this.render();
}, this);
},
onRender: function() {
console.log('render factsmenu');
}
});
var FactsLayout = Backbone.Marionette.Layout.extend({
template: tmpl['facts'],
regions: {
pages: ".pages",
filter: ".filter",
data: ".data"
},
initialize: function(options) {
this.currentPage = {};
this.factsMenu = new FactsMenu();
this.factsView = new FactsMenuView({model: this.factsMenu});
},
onRender: function() {
this.pages.show(this.factsView);
}
});
Edit:
I removed some code that made the question unclear...
The problem lies that the events of the non-collectionview of the compositeview (the modelView??) are not fired. I think this has something to do with the way the FactsLayoutView instantiates the compositeview...
The problem was caused by the way the region was rendered. In my FactsLayout is used this code:
initialize: function(options) {
this.currentPage = {};
this.factsMenu = new FactsMenu();
this.factsView = new FactsMenuView({model: this.factsMenu});
},
onRender: function() {
this.pages.show(this.factsView);
}
Apparently you can't show a view on a onRender function... I had to change the way the FactsLayout is initialized:
var layout = new FactsLayout({
slug: slug
});
layout.render();
var factsMenu = new FactsMenu({ slug: slug });
var factsView = new FactsMenuView({model: factsMenu});
layout.pages.show(factsView);
Maybe I did not understand your question well but if you need to listen an event fired from an item view within your composite view you should do like the following.
Within the item view test method.
this.trigger("test");
Within the composite view initialize method.
this.on("itemview:test", function() { });
Note that when an event is fired from an item of a CollectionView (a CompositeView is a CollectionView), it is prepended by itemview prefix.
Hope it helps.
Edit: Reading you question another time, I think this is not the correct answer but, about your question, I guess the click in the composite view is captured by the item view. Could you explain better your goal?
I am writing a backbone.js app, and I have a problem.
My collections do not fire events, can anyone spot the problem in the code bellow? I get the render-feedback, the initializer feedback.. but the append method is never called. I know that the "../app" returns a list with tro json items. And I can even see that these are being created in the collection.
Why do my event not get called?
window.TablesInspectorView = Backbone.View.extend({
tagName: "div",
initialize: function () {
console.log('Initializing window.TablesInspectorView');
// setup the tables
this.data = new Backbone.Collection();
this.data.url = "../app";
this.data.fetch();
// some event binds..
this.data.on("change", this.render , this);
this.data.on("add" , this.append_item, this);
},
render: function(){
console.log("render");
_.each(this.data.models, this.append_item);
},
append_item: function(item) {
console.log("appended");
}
});
According to my knowledge , the backbone fetch() is an asynchronous event and when it completes the reset event is triggered ,
When the models belonging to the collection (this.data) are modified , the change event is triggered, so im guessing you have not got that part correct.
so i would do something like this :
window.TablesInspectorView = Backbone.View.extend({
tagName: "div",
initialize: function () {
console.log('Initializing window.TablesInspectorView');
// setup the tables
this.data = new Backbone.Collection();
this.data.url = "../app";
this.data.fetch();
// some event binds..
this.data.on("reset", this.render , this); //change here
this.data.on("add" , this.append_item, this); // i dont see a this.data.add() in you code so assuming this was never called ?
},
render: function(){
console.log("render");
_.each(this.data.models, this.append_item);
},
append_item: function(item) {
console.log("appended");
}
});
I am working on a nested backbone view, in which if you click on, it will create a new instance of the same view. I want to disable only a specific event, not all of them; in this case, the click. I tried using undelegateEvents(), but this will disable all the functions. Any ideas on how can this be done?
Here is a piece of the code I am working on:
var View = Backbone.View.extend({
events: {
"mousedown": "start",
"mouseup": "over"
},
start: function() {
var model = this.model;
var v = new View({
model: model,
});
v.undelegateEvents(); //I just want to disable mousedown
v.render();
},
over: function() {
/*
some code here
*/
},
render: function() {
/*
some code here
*/
}
});
The idea is to ban clicking in the second instantiated view while keeping the other events. The first one will have all of its events.
Thanks
You can specify the events you want to use when you call delegateEvents:
delegateEvents delegateEvents([events])
Uses jQuery's delegate function to provide declarative callbacks for DOM events within a view. If an events hash is not passed directly, uses this.events as the source.
So you could do something like this:
var v = new View({
model: model,
});
v.undelegateEvents();
var e = _.clone(v.events);
delete e.mousedown;
v.delegateEvents(e);
v.render();
You might want to push that logic into a method on View though:
detach_mousedown: function() {
this.undelegateEvents();
this.events = _.clone(this.events);
delete this.events.mousedown;
this.delegateEvents();
}
//...
v.detach_mousedown();
You need the this.events = _.clone(this.events) trickery to avoid accidentally altering the "class's" events (i.e. this.constructor.prototype.events) when you only want to change it for just one object. You could also have a flag for the View constructor that would do similar things inside its initialize:
initialize: function() {
if(this.options.no_mousedown)
this.detach_mousedown()
//...
}
Another option would be to have a base view without the mousedown handler and then extend that to a view that does have the mousedown handler:
var B = Backbone.View.extend({
events: {
"mouseup": "over"
},
//...
});
var V = B.extend({
events: {
"mousedown": "start",
"mouseup": "over"
},
start: function() { /* ... */ }
//...
});
You'd have to duplicate the B.events inside V or mess around with a manual extend on the events as _.extend won't merge the properties, it just replaces things wholesale.
Here is a simple example that shows how to delegate or undelegate events within a Backbone view
Backbone.View.extend({
el: $("#some_element"),
// delete or attach these as necessary
events: {
mousedown: "mouse_down",
mousemove: "mouse_move",
mouseup: "mouse_up",
},
// see call below
detach_event: function(e_name) {
delete this.events[e_name]
this.delegateEvents()
},
initialize: function() {
this.detach_event("mousemove")
},
mouse_down: function(e) {
this.events.mousemove = "mouse_move"
this.delegateEvents()
},
mouse_move: function(e) {},
mouse_up: function(e) {}
})
I'm trying to familiarize myself with CoffeeScript and backbone.js, and I must be missing something.
This CoffeeScript:
MyView = Backbone.View.extend
events: {
"click" : "testHandler"
}
testHandler: ->
console.log "click handled"
return false
view = new MyView {el: $('#test_container')}
view.render()
Generates the following JavaScript:
(function() {
var MyView, view;
MyView = Backbone.View.extend({
events: {
"click": "testHandler"
},
testHandler: function() {
console.log("click handled");
return false;
}
});
view = new MyView({
el: $('#test_container')
});
view.render;
}).call(this);
But the click event does not fire testHandler when I click in test_container.
If I change the output JavaScript to:
$(function() {
var MyView, view;
MyView = Backbone.View.extend({
events: {
"click": "testHandler"
},
testHandler: function() {
console.log("click handled");
return false;
}
});
view = new MyView({
el: $('#test_container')
});
view.render;
});
Removing the call(this) and appending the $, everything works as expected. What am I missing?
(function () {}).call(this);
is just a way to immediately invoke an anonymous function while specifying a receiver. It works basically this same way as:
this.method = function () {};
this.method();
$(function () {}), at least in jQuery, is shorthand for
$(document).ready(function () {})
which runs the given function when the DOM tree has been fully constructed. It seems like this is the necessary condition for your Backbone.View.extend function to work properly.
For using CoffeeScript and jQuery together for application scripts, put your code in this sort of template:
$ = jQuery
$ ->
MyView = Backbone.View.extend
events:
"click": "testHandler"
testHandler: ->
console.log "click handled"
false
view = new MyView el: $('#test_container')
view.render()
Try using CoffeeScript class declaration syntax, e.g.:
class BacklogView extends Backbone.View
constructor: (#el) ->
this.delegateEvents this.events
events:
"click" : "addStory"
# callbacks
addStory: ->
console.log 'it works!'
What happens when the view, or at least view.el is dynamically generated? You can call the view.delegateEvents() method to manually set the eventhandlers.
Here's similar coffeescript for rendering a ChildView in a ParentView then delegating the childView's events.
window.ParentView = Backbone.View.extend
addOne: (thing) ->
view = new ChildView({model: thing})
this.el.append view.render().el
view.delegateEvents()