i'm playing with Marionette first time.
After re-rendering ItemViews, their events not triggered.
Simple example:
App = new Marionette.Application;
App.addRegions({
headerRegion: '#header',
contentRegion: '#content',
});
App.addInitializer(function () {
this.Views = {
MainMenu : new MainMenuView(),
ContentOne : new ContentOneView(),
ContentTwo : new ContentTwoView(),
};
});
App.addInitializer(function () {
var self = this;
var eva = self.vent;
eva.listenTo(self.Views.MainMenu, 'content1', function () {
self.contentRegion.show(self.Views.ContentOne);
});
eva.listenTo(self.Views.MainMenu, 'content2', function () {
self.contentRegion.show(self.Views.ContentTwo);
});
});
App.on('start', function () {
var self = this;
self.contentRegion.show(self.View.ContentOne);
});
App.start();
After re-rendering ContentOneView & ContentTwoView, their events not triggered.
What i'm doing wrong?
The problem you are having is that region.show() is going to close any view that is currently occupying that region. Doing so undelegates the view's events. After the initial region.show() you should manually call render on the view.
You can see this explained here and an issue discussing it here.
I managed to solve this problem by using delegating the events when the view is shown in the layout:
layoutView.content.show(contentView);
contentView.delegateEvents();
Although this is only necessary after the first render as mentioned by Andrew Hubbs
instead of using eva to listen to events that happen on the views, try listening to eva for events passed by other views
App.addInitializer(function () {
var eva = self.vent;
var self = this;
this.listenTo(eva, 'someStringHere', function(){/*do stuff here*/};
});
and then in your views you can trigger events through eva/vent
var eva = self.vent;
eva.trigger("someStringHere");
Related
my problem is to bind a model, that I istanciate at certain time during my application, to a View that has been created at initialization time. Let me explain better:
In my route I istanciate a View:
...
var view = new app.FirstView();
view.render();
In
app.FirstView = Backbone.View.extend({
initialize: function(){
...
this.SidebarView = new app.SidebarView();
}
... });
At certain time X during my application I have this behaviour in an another view:
app.AnotherView = Backbone.View.extend({
...
onClickHandler: function(){
var aModel = app.aModel();
var view = app.aView({ model: aModel });
}
})
My problem now, is to bind aModel defined in the onClickHandler to SidebarView which I created and bounded to the FirstView at initialization time.
Please tell me if something is not clear.
Thanks in advance
one solution is to use an event for example,
In your AnotherView trigger a event and pass to model to View
onClickHandler: function(){
var aModel = app.aModel();
Backbone.trigger('model:assigned',aModel);
}
in your sidebarView's initialize you should listen for event
Backbone.on("model:assigned",function(passedModel){
this.model = passedModel;
})
Note1: You must be sure that sidebarView is initialized by the time you are triggering event.
Note2: Try avoiding Global events , i used as i don't know your code structure.
Good morning,
i have a mootools class with the event 'beforeunload' associate to the window object.
i need to add similar event when my partial dom container change/remove.
There is a way to intercept this event?
This is my class code:
var Push = new Class({
Implements: [Events],
attachPushClassEvents: function() {
var self = this;
window.addEvent('beforeunload', function(event){
// do something
});
},
initialize : function(domContainer) {
this.container = domContainer;
self.attachPushClassEvents();
}
});
Thanks in advance
I'm trying to manually trigger a click event right after the html has been rendered but it's not working.
To simplify and verify that it's not working I tried this code:
var _testView = Backbone.View.extend({
events : {
'click a' : 'sayHi'
},
initialize : function() {
this.render();
this.$el.find('a').trigger('click');
},
render : function() {
$(document.body).html(
this.$el.html('alert hi')
);
},
sayHi : function() {
alert('Hi');
return false;
}
});
var y = new _testView;
I'm trying to manually trigger the click event but it's not being triggered. If I'm going to put the trigger in a setTimeout with a delay of 500 it will work. I don't know why.... thx
I found the answer. I looked at the Backbone core and I see that initialize method is being called first before attaching the events to the view.
View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
this._configure(options || {});
this._ensureElement();
this.initialize.apply(this, arguments);
this.delegateEvents();
};
You are calling the click event on the element which haven't been yet created. You should call the function when the render is finished or you can just call this.sayHi() instead of triggering the click.
hjuster is correct. the $(document.body) wait for the 'document ready' event, and you are calling new _testView before the document is ready. You can change your intialize to this - it queues up the trigger to execute after the document is ready.
initialize : function() {
this.render();
var self = this;
$(function(){
self.$el.find('a').trigger('click');
});
},
I added the var 'self' since you can't reference 'this' to get your _testView object in the function.
It works in this fiddle
I got following example Backbone.View:
var View = Backbone.View.extend({
tagName: "div",
className: "advertisement",
initialize: function () {
this.on('switch', this.
switch, this);
this.views = {};
this.views.laptop = new LaptopAdView();
this.views.piano = new PianoAdView();
this.views.food = new FoodAdView();
},
render: function () {
this.$el.empty();
this.
switch ('laptops');
return this;
},
switch: function (ad) {
var el;
if (ad === 'laptop') {
el = this.views.laptop.render().el;
} else if (ad === 'piano') {
el = this.views.piano.render().el;
} else {
el = this.views.food.render().el;
}
// reinsert the chosen view
this.$el.empty().append(el);
// bind all events new
this.delegateEvents();
}
});
As you see I use this.delegatEvents() to recreate all event bindings. This is actually not a perfect solution... It would be far better when I use this.$el.detach(); or an other method so I cache the whole object with its event instead of re-rendering and recreating all events.
Now with working fiddle:
http://jsfiddle.net/RRXnK/66/
You are trying to bind to a Backbone view instead of a DOM element:
this.on('switch', this.switch, this);
It's all good, but who triggers 'switch' event? You do not have bindings that trigger DOM events, and this.delegateEvents(); only works on DOM events.
Once you set up DOM events, e.g.
this.$el.on('click', 'a', this.switch) -- 'switch' event will be triggered, and you will NOT have to re-delegate events as long as $el is in not removed from DOM.
The 2nd answer to this question nicely explains how event declarations in Backbone.js views are scoped to the view's el element.
It seems like a reasonable use case to want to bind an event to an element outside the scope of el, e.g. a button on a different part of the page.
What is the best way of achieving this?
there is not really a reason you would want to bind to an element outside the view,
there are other methods for that.
that element is most likely in it's own view, (if not, think about giving it a view!)
since it is in it's own view, why don't you just do the binding there, and in the callback Function,
use .trigger(); to trigger an event.
subscribe to that event in your current view, and fire the right code when the event is triggered.
take a look at this example in JSFiddle, http://jsfiddle.net/xsvUJ/2/
this is the code used:
var app = {views: {}};
app.user = Backbone.Model.extend({
defaults: { name: 'Sander' },
promptName: function(){
var newname = prompt("Please may i have your name?:");
this.set({name: newname});
}
});
app.views.user = Backbone.View.extend({
el: '#user',
initialize: function(){
_.bindAll(this, "render", "myEventCatcher", "updateName");
this.model.bind("myEvent", this.myEventCatcher);
this.model.bind("change:name", this.updateName);
this.el = $(this.el);
},
render: function () {
$('h1',this.el).html('Welcome,<span class="name"> </span>');
return this;
},
updateName: function() {
var newname = this.model.get('name');
console.log(this.el, newname);
$('span.name', this.el).text(newname);
},
myEventCatcher: function(e) {
// event is caught, now do something... lets ask the user for it's name and add it in the view...
var color = this.el.hasClass('eventHappened') ? 'black' : 'red';
alert('directly subscribed to a custom event ... changing background color to ' + color);
this.el.toggleClass('eventHappened');
}
});
app.views.sidebar = Backbone.View.extend({
el: '#sidebar',
events: {
"click #fireEvent" : "myClickHandler"
},
initialize: function(){
_.bindAll(this, "myClickHandler");
},
myClickHandler: function(e) {
window.user.trigger("myEvent");
window.user.promptName();
}
});
$(function(){
window.user = new app.user({name: "sander houttekier"});
var userView = new app.views.user({model: window.user}).render();
var sidebarView = new app.views.sidebar({});
});
Update: This answer is no longer valid/right. Please see other answers below!
Why do you want to do this?
Apart from that, you could always just bind it using regular jQuery handlers. E.g.
$("#outside-element").click(this.myViewFunction);
IIRC, Backbone.js just uses the regular jQuery handlers, so you're essentially doing the same thing, but breaking the scope :)