Event does not bind when adding view dynamically - backbone.js

I have two simple views, one with a button that creates a view and append it to the page. The new view consists of a single list item with a link and an event to I need to bind to each list item. I think the problem here is the el object: What I have been reading the el object should be created automatically when it's not defined on construction of the view? See this fiddle
HTML:
<div id="main">
<button type="button" class="add">Add view</button>
<ul id="tasks" />
</div>
<script id="view-template-new-task" type="text/html">
<li>Task</li>
</script>
​
JS:
var TaskView = Backbone.View.extend({
events: {
'click a.fire': 'fire'
},
fire: function() {
alert('fire');
},
initialize: function() {
this.template = _.template($('#view-template-new-task').html());
},
render: function() {
$('#tasks').append(this.template());
}
});
var View = Backbone.View.extend({
events: {
'click button.add': 'addView'
},
addView: function(e) {
var task = new TaskView();
task.render();
}
});
$(function() {
var view = new View({
el: $('#main')
});
});​

Backbone automatically delegates events to the view element. As is, the el in your TaskView would point to an unattached div (the default el created by Backbone) and not to an element in your list.
The cure is simple : create your subview with its el set to a correct DOM node by setting a tagName to li and appending this element in your main view.
var TaskView = Backbone.View.extend({
tagName: 'li',
events: {
'click a.fire': 'fire'
},
fire: function() {
alert('fire');
},
initialize: function() {
this.template = _.template($('#view-template-new-task').html());
},
render: function() {
this.$el.html(this.template());
return this;
}
});
var View = Backbone.View.extend({
events: {
'click button.add': 'addView'
},
addView: function(e) {
var task = new TaskView();
this.$('#tasks').append(task.render().el);
}
});
And an updated Fiddle http://jsfiddle.net/BLP6J/31/

Related

Backbonejs: triggering events when inserting svg

Why does this not work?.........
SpiderView = Backbone.View.extend({
el: $('#mobidim'),
events: {
'click path': 'focusItem'
},
initialize: function(){
this.render();
},
render: function(){
$(this.el).html('<embed src="img/mobile-dimensions.svg" type="image/svg+xml" />');
},
focusItem: function(event ) {
console.log("yeeeeeeeeee");
}
});
var spider = new Spider;
var spiderView = new SpiderView();
-> Goal in the end is, that I can trigger events from items within the SVG graphic and then do some manipulation on the graphic (simple like hiding and showing nodes within the SVG)
Just to answer this question:
In my HTML I added:
`<script type="text/template" id="svg_template">
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1052.3622"
...
...
</script>
`
And I then changed my view to this:
SpiderView = Backbone.View.extend({
el: $('#mobidim'),
events: {
'click path': 'focusItem'
},
initialize: function(){
this.render();
},
render: function(){
// Compile the template using underscore
var template = _.template( $("#search_template").html(), {} );
// Load the compiled HTML into the Backbone "el"
this.$el.html( template );
},
focusItem: function(event ) {
console.log("yeeeeeeeeee");
}
});
Now I can add my click events.

Multiple Views and Sub Views with 1 collection in Backbone

I have issue in rendering Shopping Bag Views using Backbone for my website.
I am using 1 collection for all Bag Views (“Quick_View” of items list & “Normal_View” of Items list). Also I created “Item_View”, which is being used to render each item in both the Views.
It is a SPA (Single Page Application) and “Quick_View” is initiated and rendered for all Backbone routes and hidden by default. Whenever user clicks on “Quick_View” link from any page it is showing. There is no route defined for it.
The “Normal_View”, can be accessed using Checkout button given in “Quick_View”. It is bind with “domain/checkout” route.
When I access “Normal_View” from “Quick_View” check button; it works fine and both (Quick and Normal) views are in Sync. Means, when we add, delete, update any item in any of the View, both Views are getting updated accordingly.
But when I access “domain/checkout” route directly in a new browser, both views are getting rendered fine, but they are not in sync. Means, change in 1 view does not update another view.
The reason, I tracked is, when I access “Normal_View” through “Quick_View”, model for each item in both the Views having same CID, so the both Views are in sync, if there is any change in a model from any of the View.
And, when I access “Normal_View” directly, model for each item in both the views are not having same CID, so they do not work as expected.
There are few more points to consider:
Collection is firing reset event twice for “Quick_View” and each item
in “Quick_View” is rendering twice.
When, I access “Normal_View” (in either way), “Quick_View” is again getting rendered but once “Normal_View” rendering is over.
// Main View
var mainView = Backbone.View.extend({
el: 'body',
template: {
header: Handlebars.compile(headerTemplate),
mainNav: Handlebars.compile(mainNavtemplate),
footer: Handlebars.compile(footerTemplate)
},
initialize: function() {
_.bindAll();
AW.collection.bag = new bagCollection();
//AW.collection.bag.fetch({reset:true});
},
render: function() {
this.$el.html(this.template());
this.loadSubView('bagQV');
},
loadSubView: function(subView) {
switch(subView) {
case 'home' :
if(!AW.view.home) AW.view.home = new homepageView();
AW.view.home.render();
break;
case 'bagQV' :
if(!AW.view.bagQV) AW.view.bagQV = new bagQuickView({collection: AW.collection.bag});
//AW.view.bagQV.render();
break;
case 'checkout' :
if(!AW.view.checkout) AW.view.checkout = new checkoutView({collection: AW.collection.bag});
AW.view.checkout.render();
break;
}
}
});
// Single Item View
var bagItemView = Backbone.View.extend({
tagName: 'tr',
template: Handlebars.compile(bagItemTemplate),
initialize: function() {
_.bindAll(this);
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'remove', this.removeItem);
$(document).on('keyup', this.listenKeyboard);
},
events: {
'click .qtyInput .button' : 'updateItem',
'click .controls a.remove' : 'removeModel'
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
this.$el.attr('data-id',this.model.cid);
return this;
},
updateItem: function(e) {
e.preventDefault();
e.stopPropagation();
var newQty = this.$el.find('.qtyInput input').val();
var newAmt = newQty * parseFloat(this.model.get('prodRate').replace('$',''));
this.model.set({prodQty: newQty, amount: '$' + newAmt});
this.cancelEdit(e);
},
removeModel: function(e) {
e.preventDefault();
e.stopPropagation();
if(AW.collection.bag) AW.collection.bag.remove(this.model);
},
removeItem: function() {
this.$el.remove();
}
});
// Bag Quick View
var bagQuickView = Backbone.View.extend({
tagName: 'div',
id: 'myBagQV',
template: Handlebars.compile(bagQuickViewTemplate),
initialize: function() {
_.bindAll(this);
this.collection.fetch({reset:true});
//this.collection.bind("reset", _.bind(this.render, this));
this.listenTo(this.collection, 'add', this.addItem);
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
if($('#myBagQV').length == 0) {
this.$el.html(this.template());
$('body').append(this.el);
}
this.addAllItems();
return this;
},
addItem: function(item) {
var parent = this;
var itemView = new bagItemView({model: item});
$('#itemsInBag table tbody').append(itemView.render().el);
},
addAllItems: function() {
if(this.collection.length > 0) {
$('#itemsInBag table tbody').html('');
this.collection.each(this.addItem, this);
}
},
});
// Normal Bag View
var bagView = Backbone.View.extend({
tagName: 'div',
id: 'myBag',
template: Handlebars.compile(checkoutBagTemplate),
initialize: function() {
_.bindAll(this);
this.collection.fetch({reset:true});
//this.collection.bind("reset", _.bind(this.render, this));
this.listenTo(this.collection, 'add', this.addItem);
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.$el.html(this.template());
$('#checkoutContainer #details').append(this.el);
this.addAllItems();
return this;
},
addItem: function(item) {
var parent = this;
var itemView = new bagItemView({model: item});
this.$el.find('table tbody').append(itemView.render().el);
},
addAllItems: function() {
if(this.collection.length > 0) {
this.$el.find('table tbody').html('');
this.collection.each(this.addItem, this);
}
}
});
Looking for you help.
Thank you in advance
Cheers,
Vikram

Nested views in Backbonejs, a post & comments relation

Array Of Objects
The data is received from server
var Updates = [
{"post_id":"1","post_desc":"This is my first post",
"comments":[{"id":1,"comment":"some comments","like":7},
{"id":9,"comment":"some comments","like":3}
]
},
{"post_id":"2","post_desc":"This is my second post",
"comments":[{"id":5,"comment":"some comments","like":5}]
}]
Model:
var Update = Backbone.Model.extend({
defaults:{
photo: "default.png"
}
});
Collection:
var latestUpdates = Backbone.Collection.extend({
model: Update
});
Single View:
var UpdateView = Backbone.View.extend({
tagName: "div",
className: "post-container",
template: $("#postTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
Master view:
var UpdatesView = Backbone.View.extend({
el: $("#postContainer"),
initialize: function () {
this.collection = new latestUpdates(Updates);
this.render();
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.renderUpdates(item);
}, this);
},
renderUpdates: function (item) {
var updateView = new UpdateView({
model: item
});
this.$el.append(updateView.render().el);
}
});
//create app instance
var wallUpdates = new UpdatesView();
How can I render comments section under each post?
Trying to achieve layout similar to facebook post-comment system
I'd use a CommentListView, owned by your UpdateView. tagName: "ul", className: "post-comments"
Then have a CommentView owned by the CommentListView. CommentView's render should not append anything to the DOM, but return its $el.
CommentListView would tell each of the CommentView's to render, appending each of their $el's to the CommentListView's $el.
For the containers, I'd use:
<div class="post-container" data-post-id="<%= YourPostId %>">
<div class="post-body">
<!--Your post can go in here-->
</div>
<ul class="post-comments">
<!--Append your comments in here-->
</ul>
</div>

onclick event updating the model attribute but the view not rendering

In my backbone, i made a onclick event to each model, to update the name.. that works fine. also i am getting the trigger in my view to update the view of the model..
But the view not updating the model.. and the text not at all changing in the html..
my model view:
var studentView = Backbone.View.extend({
tagName:'li',
events:{
'click':"called"
},
template:_.template($("#studentTemplate").html()),
render:function(){
this.$el.append(this.template(this.model.toJSON()));
this.model.get('scored') > 60 ? this.$el.addClass("active") : null;
return this;
},
called:function(){
this.model.set('name','text'); // i am setting a name as 'text' for example
}
});
my render View:
var studentsView = Backbone.View.extend({
el:$(".page"),
events:{
"click #highScoreBtn":"showHighScore"
},
initialize:function(){
this.collection = new collection(student);
this.render();
this.collection.on('change', this.renderOne,this);
},
render:function(){
var that = this;
_.each(this.collection.models, function(item){
that.$el.find('ul').append(new studentView({model:item}).render().el);
})
},
renderOne:function(data){
this.$el.find('ul').append(new studentView({model:data}).render().el); // appending as new element instead updating the existing one..
}
})
so, what is wrong with my code.. or any one correct me this in my jsfiddle..
here is my jsfiddle link
Thanks in advance..
this is works me:
var studentView = Backbone.View.extend({
tagName:'li',
events:{
'click':"called"
},
template:_.template($("#studentTemplate").html()),
initialize:function(){
this.listenTo(this.model, 'change', this.render);
},
render:function(){
this.$el.html(this.template(this.model.toJSON()));
this.model.get('scored') > 60 ? this.$el.addClass("active") : null;
return this;
},
called:function(){
this.model.set('name','text');
}
});

How to bind elements in backbone?

I have a small backbone class:
view = Backbone.View.extend({
events: {
"click textarea" : "doSomething"
},
doSomething : function() {
var textarea = $(this.el).find('textarea')
// I would like to just call, this.textarea, or this.elements.textarea
}
});
Ideally I would like to be able to access my textarea through a variable instead of having to search the element every time. Anyone have any idea on how to accomplish this?
maybe i am under thinking it but how bout giving the textarea a class or id and target specifically when needed,
or create a sub view that generates the textarea
var View = Backbone.View.extend({
el: '#main',
initialize: function(){
this.render();
},
render: function() {
var subview = new SubView();
this.$('form').append(subview.el);
this.$('form').show();
},
});
var SubView = Backbone.View.extend({
tagName: 'textarea',
id: 'whateverId',
events: {
"click" : "doSomething"
},
initialize: function(){
this.render();
},
doSomething : function(event) {
var textarea = $(event.target);
// or var textarea = $(this.el);
},
render: function(){
return $(this.el);
}
});
The other answers get you the reference that you need, but if you really need a handle to the textarea, then you can do something like this:
view = Backbone.View.extend({
initialize: function() {
this.myTextareaElement = $(this.el).find('textarea')
}
events: {
"click textarea" : "doSomething"
},
doSomething : function() {
// use this.myTextareaElement ...
}
});
pass the event as the argument and then use the event target
view = Backbone.View.extend({
events: {
"click textarea" : "doSomething"
},
doSomething : function(event) {
var textarea = $(event.target);
}
});

Resources