So here I have two views addBook and showBooks , the idea is, as I click on a button it should add a book name and author to the DOM its an , and I should be able to click it. But the problem is for each element more than one event handlers are added. that is for each new inner view more than one click events are added, I am quite new in backbone and I am still in Dark
here is my code , first the outer view
var addBook = Backbone.View.extend({
el : $('#addbook'),
model : bookmodel,
template : _.template(addbookTemplate,{}),
initialize : function(val){
this.el.html(this.template);
bookcollection.bind('add', this.showOff)
},
render : function(){
},
events : {
'click #addbookbutton' : 'addHandler'
},
addHandler : function(){
var book = new this.model;
book.addValues($('#bookname').val(), $('#bookauthor').val());
bookcollection.add(book);
},
showOff : function(buk){
this.xx = new showval({model: buk});
$('#fatman').append((this.xx.render().el));
}
});
return addBook;
Now the second and inner view
var showbooks = Backbone.View.extend({
events : {
'click .individual' : 'deleteHandler'
},
initialize : function(values){
_.bindAll(this);
/*if(values){*/
//return this.template;
//}
//values = (values == "") ? {} : values;
this.el.append(this.template);
},
render : function(){
this.el = _.template(show, {name : "Max", author : "jk"});
this.delegateEvents();
return this;
},
deleteHandler : function(e){
alert("this kills me")
}
});
return showbooks;
Now here is the problem as I click the button is added to DOM button but for each new li one more alert is fired ... Help is highly appreciated , I know I am missing some crucial point .. !!
I have taken your code and changed it slightly. I have created 2 Backbone.View's, one as the container and one for book items. It's crude, but hopefully will give you an idea of where you have gone wrong. http://jsfiddle.net/cQEu2/
Related
i want to fire on_change events on dynamically created drop boxes.
but have no idea how to do it in backbone js
here is my html code creating a div tag
<div id="page">
<input type="button"id="btn1"value="ok">
</div>
and its my backbone code where i am dynamically adding drop down in
var btn2id ="";
var app = {};app.v1 = Backbone.View.extend({
el: '#page',
events: {
'click #btn1' : 'f1',
},
f1:function()
{
alert("Boom");
btn2id="btn2";
for(var j=0;j<3;j++) {
$('#page').append('<select id="selecty'+j+'"></select>');
for(var i=0;i<10;i++){
$('#selecty'+j+'').append('<option value="'+i+'">'+i+'</option>');
}
vv = new app.v2();}}
}
});
app.v2 =Backbone.View.extend({
el: '#page',
events:{
at this place i have no idea what to do
// for(int i=0;i<3;i++){
// 'change '#selecty'+i+'' : 'f2',
// }
},
f2:function() {
alert("Boom again");
}
v = new app.v1();
});
v = new app.v1();
In my opinion, reusable components should have their on view.
This practice lets you bind the recurring events easily, and in general matter cleans your code.
Note: in my code example I didn't use any template engine or practice, but I totally recommend you to do that.
So lets assume you have the main view with a button that creates new select elements:
var View = Backbone.View.extend({
el : "#main",
events : {
'click #add' : 'add',
},
add : function(){
var select = new SelectView();
this.$el.append(select.render().el);
}
});
As you can see, anytime #add is clicked, it creates a new SelectView which represents the select element.
And the select element itself:
var SelectView = Backbone.View.extend({
events:{
'change select' : 'doSomething'
},
doSomething: function(e){
$(e.currentTarget).css('color','red');
},
render: function(){
this.$el.html("<select />");
for(var i=0;i<10;i++)
{
this.$el.find('select').append("<option value='"+i+"'>"+i+"</option>")
}
return this;
}
});
In my dummy example I just change the color of the element when it is changed. You can do whatever.
So, it is now super easy to bind events to the select views.
In general, I would recommend you that when you are working with reusable components, you should always think of a practice which makes things make sense.
This is one of many ways to do that, but it is pretty simple to understand and implement.
You are welcome to see the "live" example: http://jsfiddle.net/akovjmpz/2/
I have created an extensive app using Backbone. So far, everything works very well. However, when I refresh/reload a page on a given hash (e.g. myapp/#dashboard), the view is rendered twice and all events are bound twice. If I go to another section and come back. everything is working normally.
I use a subrouter that looks like this:
var DashboardRouter = Backbone.SubRoute.extend({
routes : {
/* matches http://yourserver.org/books */
"" : "show",
},
authorized : function() {
// CODE TO RETRIEVE CURRENT USER ID ...
return (lg);
},
show : function() {
var usr = this.authorized();
if (this.dashboardView) {
this.dashboardView.undelegateEvents();
}
switch(usr) {
case 2:
this.dashboardView = new StudentDashboard();
break;
case 3:
this.dashboardView = new CounsellorDashboard();
break;
case 4:
this.dashboardView = new AdminDashboard();
break;
default:
location.replace('#signout');
}
},
});
I have checked within the console, and the events here are called only once. The student dashboard looks like this (extract)
DashboardView = Backbone.View.extend({
el : "#maincontent",
template : tpl,
model : new Student(),
events : {
"click #edit-dashboard" : "editDashboard",
"click #add-grade" : "addGrade",
"click #add-test" : "addTest",
"click #add-eca" : "addECA"
},
initialize : function() {
// BIND EVENTS
_.bindAll(this, 'render', 'editDashboard', 'renderGrades', 'renderTests', 'renderECAs', 'renderPreferences');
this.model.on("change", this.render);
this.model.id = null;
this.model.fetch({
success : this.render
});
},
render : function() {
$(this.el).html(this.template(this.model.toJSON()));
// set location variables after main template is loaded on DOM
...
if (!this.gradeList) { this.renderGrades(); };
if (!this.testList) { this.renderTests(); };
if (!this.ecaList) { this.renderECAs(); };
if (!this.preferencesView) { this.renderPreferences(); };
this.delegateEvents();
return this;
},
From the console logs I know that all the subviews are rendered normally only once, but twice when I refresh the page, and I have no idea why.
You need to make sure all events on your view are undeligated before re-rendering it.
Add following function inside your views.
cleanup: function() {
this.undelegateEvents();
$(this.el).empty();
}
Now, in your router before rendering the view, do the cleanup if the view already exists.
if (this.myView) { this.myView.cleanup() };
this.myView = new views.myView();
I create one sign in view, and put it in other main views.
Here is one of a main view :
define(["jquery" ,
"underscore" ,
"backbone" ,
"webconfig",
"text!templates/header.html",
"text!templates/signInTemplate.html" ,
"text!templates/miniSignInTemplate.html",
"text!templates/footer.html"
],function($ , _ , Backbone, WebConfig, HeaderTem, SignInTem, MiniSignInTem, FooterTem){
var signInView = Backbone.View.extend({
initialize : function(){
},
el: '#webbodycontainer',
render : function(){
this.$el.empty();
var headerTem = _.template(HeaderTem);
var signInTem = _.template(SignInTem);
var miniSignInTem = _.template(MiniSignInTem);
var footerTem = _.template(FooterTem);
$("#webheader").html(headerTem);
this.$el.html(signInTem);
$("#rightpanel").html(miniSignInTem);
$("#webfooter").html(footerTem);
}
});
return signInView;
});
And here is a sub view :
define(["jquery" ,
"underscore" ,
"backbone" ,
"webconfig",
"text!templates/miniSigninTemplate.html"
],function($ , _ , Backbone , WebConfig , signinTemp){
var MiniView = Backbone.View.extend({
initialize:function(){
this.render();
},
event : {
'click #signIn' : 'signInUser'
},
signInUser : function(){
alert(1);
},
render:function(){
var template = _.template(signinTemp)
this.$el.html(template);
}
});
return MiniView;
});
Routing main view :
app_router.on('route:signInAction', function(signIn){
if(window.currentSection)
window.currentSection.remove();
window.currentSection = new SignIn({});
$("#webbodycontainer").html(window.currentSection.$el);
window.currentSection.render(signIn);
});
Problem : signInUser in sub view is not working also the whole view (MiniView) is not called. Did I miss something?
Any help would be much appreciated, thank you..
In what you have written here, you can render the view by doing something like this:
var singInView = new SignIn();
enter code heresingInView.render();
You will not need these lines:
window.currentSection = new SignIn({});
// unnecessary, just create a new object when needed
// plus, binding objects to window is totally not standard
$("#webbodycontainer").html(window.currentSection.$el);
// when you call render() on a new object, it binds it to the dom
// you do not need this line either
window.currentSection.render(signIn);
// why are you passing signIn to render? this doesn't do anything
In short, you can call your view and render it in a one liner (if you are not passing any other arguments to the view) like this:
(new SignIn()).render();
If you want to pass arguments to the view, again you can do it in a one liner:
(new SignIn({
key: value,
key: value
})).render();
I have a series of comments on a page, which can be edited. My idea was to render the comments by Rails and preload a json with all those comments in a Backbone Collection.
Then I would poll every x seconds, to see if there are changes. Normally I render the collection by looping over all the models, and create a view for each item. When a model gets updated, so will the view (comment im this case).
But my question is this, how do you bind a view to the model, when the view is already in the DOM. Especially since the view had a dynamic id. There is no point in rendering the view, since it's already there. When you render a view, backbone binds it through somekind of cid.
The only solution I can think of, is by setting an id in the dom object on pageload. iow
<div id="comment-<%= content.id %>"></div>
. And then in the initialize of the view, reset the id
class Comment extends Backbone.View
initialize: ->
#id = "comment-" + #model.get('id')
But I'm not sure if thats the way to go. Would events still be binded?
Special for you :)
var CommentsView = Backbone.View.extend({
tagName : 'ul',
comments : {},
initialize : function () {
this.listenTo(this.collection, 'add', this.addComment);
this.listenTo(this.collection, 'remove', this.removeComment);
this.listenTo(this.collection, 'change', this.updateComment);
},
addComment : function (model) {
this.comments[model.id] = new CommentView({model:model});
this.$el.append(this.comments[model.id].render().$el);
},
removeComment : function (model) {
this.comments[model.id].remove();
this.comments[model.id] = null;
},
updateComment : function (model) {
this.comments[model.id] = new CommentView({model:model});
this.$('[data-id="' + model.id + '"]').before(this.comments[model.id].render().$el).remove();
}
});
var CommentView = Backbone.View.extend({
tagName : 'li',
template : _.template('<div data-id="<%= id %>"><%= name %>: <%- message %></div>'),
render : function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
// comments
var initialComments = [{id:1, name:'user1', message:'great!'}, {id:2, name:'user2', message:':)'}];
var actualComments = [{id:1, name:'user1', message:'great! [edited]'}];
var comments = new Backbone.Collection();
var commentsView = new CommentsView({collection:comments});
// show comments
commentsView.render().$el.appendTo('body');
// simulate fetch
comments.add(initialComments);
// simulate update
_.delay(function() {
comments.update(actualComments);
},
2000);
jsfiddle
Good morning,
I try these days to make AgilityJS work, and i'm still stuck with these two naturals events : add and remove.
(function(window){
var Test = $$({
model : {},
view : {
format : '<div></div>'
},
controller : {
'create' : function(){
console.log('create');
},
'add' : function(){
console.log('add');
},
'remove' : function(){
console.log('remove');
}
}
});
$(document).ready(function(){
$$.document.append(Test, '#test');
var t = setTimeout(function(){
console.log('time out');
$$.document.remove(Test);
}, 1000);
});
})(window);
I've got in my html a div with an id of #test.
Do someone knows how to make them work?
Thanks.
This events are not referring to the moment when the object is added to another one (like window) but they are triggered when you insert another agility object inside the one with binds.
The event remove are working just fine but the event add was replaced by events append and prepend in this commit: https://github.com/arturadib/agility/commit/1b2483333dde3f55b3305f2746e4dd6730a1c364
You can see an example of the remove and append events here:
http://jsbin.com/welcome/14942/edit