why the drawSomething doesn't work - backbone.js

I use the backbone to test something, but i don't know why the drawSomething just no show ##"
initialize: function() {
setInterval(function() {
//alert("Hello");
this.drawSomething();
}, 1000);
},
drawSomething: function() {
alert('hi');
},

The problem is that inside setInterval callback context this is not what you expect (it's global object window). Simplest fix is to save proper object reference in variable:
var self = this;
setInterval(function() {
//alert("Hello");
self.drawSomething();
}, 1000);

as you are using backbone, so possible you are using underscore too. Bind should help:
initialize: function () {
var foo = function () { this.drawSomething(); };
foo = _.bind(foo, this);
setInterval(foo, 1000);
}
or jQuery analog Proxy:
foo = $.proxy(foo, this);

as quick solution
initialize: function() {
setInterval(function() {
//alert("Hello");
this.drawSomething();
}.bind(this), 1000);
},
drawSomething: function() {
alert('hi');
},
but I would prefer to use additional variable as dfsq methioned, because some old browsers doesn't support bind

Related

Issue with el backbone view

I don't understand why in my view el is defined only into loadResults function and not in checkScroll...may depends on documentAddEventLIstener("scroll",this.checkScroll,this)?
I can't understand reason
var HomePostView = Backbone.View.extend({
tagName: "ul",
// id: "list",
// el:$('.table-view'),
template: Handlebars.compile(template),
events: {
'scroll': 'checkScroll'
},
initialize: function () {
//console.log(this.collection);
// this.collection.bind("add", this.render, this);
document.addEventListener("scroll", this.checkScroll, this);
this.isLoading = false;
this.IntegratedCollection= new Integrated();
this.IntegratedCollection.twitterQuery=11265832;//spostare in load results
this.IntegratedCollection.fetch();
this.listenTo(this.IntegratedCollection,'add',this.render);
console.log((this.el));
},
render:function(){
this.loadResults();
},
loadResults: function (eventName) {
console.log((this.el));<-----WELL DEFINED HERE
this.isLoading = true;
$(this.el).empty();
_.each(this.IntegratedCollection.models, function (a) {
$(this.el).append(new SingleHomePostView({
model: a
}).render().el);
}, this);
this.isLoading = false;
return this;
},
setParameters: function(){
this.IntegratedCollection.page += 1; // Load next page
this.IntegratedCollection.twitterQuery=11265832;
this.IntegratedCollection.fetch();
},
checkScroll: function () {
console.log(this.el);<-----UNDEFINED HERE
var triggerPoint = 100; // 100px from the bottom
if( !this.isLoading && this.el.scrollTop + this.el.clientHeight + triggerPoint > this.el.scrollHeight ) {
console.log("inside");
// this.setParameters();
//this.loadResults();
}
}
});
I think your 'this' is pointing to window object. Try
that = this;//In initialize
and use that instead of this in checkScroll.
It is because of this:
document.addEventListener("scroll", this.checkScroll, this);
The DOM addEventListener function does take three arguments, but the third one is not a context argument, it is a boolean telling it whether to process events in the capture phase of event propagation. Instead, you can bind the event handler with the proper context:
document.addEventListener("scroll", this.checkScroll.bind(this));

UnderscoreJS nested templates: How do I do it?

I'm not faced with a technical challenge per se as I have some working code. I'm just not sure this is the right way to go so I'd like to run by some experts before I continue down this path...
I'm using the 'render' function from this post: https://stackoverflow.com/a/10136935/1480182
I then have two Backbone views:
DetailLineView = Backbone.View.extend({
initialize: function (options) {
this.options = options;
this.render();
},
render: function () {
var variables = { detailLine: this.options.detailLine };
this.$el.html(render("DetailLine", variables));
}
});
and
CustomerView = Backbone.View.extend({
initialize: function (options) {
this.options = options;
this.render();
},
render: function () {
var dl = "";
_.each(this.options.customer.attributes.detailLines, function (line) {
var v = { detailLine: line };
dl += render("DetailLine", v);
});
var variables = { customer: this.options.customer.attributes, detailLinesHtml: dl };
this.$el.html(render("Customer", variables));
}
});
and of course the corresponding templates.
Now the above code works but as far as I can tell I'm not actually using the DetailLineView.
I have a feeling that there's a (much?) more elegant way of doing this but I fail to see how... can anyone help out?
EDIT: A better(?) solution:
I changed my CustomerView to this:
CustomerView = Backbone.View.extend({
initialize: function (options) {
this.options = options;
},
render: function () {
var variables = { customer: this.options.customer.attributes };
this.$el.html(renderTemplate("Customer", variables));
var dlv = new DetailLineView({
el: $('.detailLinesContainer', this.$el),
detailLine: this.options.customer.attributes.detailLines[0]
});
dlv.render();
}
});
I like it better and I'm now using my DetailLineView...
I've updated my OP with a solution that imho is better than the original.

Backbone Keypress not triggering after adding template

I've created my own version of what is basically: todomvc dependency-example, but I built it from looking at this Modular Backbone Example. I'm trying to move hardcoded html in the base template to it's own template file. I plan on creating multiple pages and want a minimal base template. However, when I load and insert the template in the view, the keypress event for createOnEnter stops working. Every other feature still works, which includes the other event listed in events (clearCompleted).
See: this.$el.append(notesTemplate);.
The browser never makes it to the function createOnEnter().
My app view:
define([
'jquery',
'underscore',
'backbone',
'models/notes/NoteModel',
'collections/notes/NoteCollection',
'views/notes/NotesListView',
'text!templates/notes/statsTemplate.html',
'text!templates/notes/notesTemplate.html'
], function($, _, Backbone, NoteModel, NoteCollection, NotesListView, statsTemplate, notesTemplate){
'use strict';
var NotesView = Backbone.View.extend({
el: $("#page"),
events: {
"keypress #new-note": "createOnEnter",
"click #clear-completed": "clearCompleted"
},
initialize: function() {
var onDataHandler = function(collection) {
this.render();
}
this.$el.append(notesTemplate);
this.model = new NoteCollection();
this.model.fetch({ success : onDataHandler, dataType: "jsonp"});
this.input = this.$("#new-note");
this.allCheckbox = 0;
this.listenTo(this.model, 'add', this.addOne);
this.listenTo(this.model, 'reset', this.addAll);
this.listenTo(this.model, 'all', this.render);
this.footer = this.$('footer');
this.main = $('#main');
this.model.fetch();
},
render: function() {
var done = this.model.done().length;
var remaining = this.model.remaining().length;
if (this.model.length) {
this.main.show();
this.footer.show();
this.$('footer').show();
this.footer.html(_.template(statsTemplate, {done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
this.allCheckbox.checked = !remaining;
},
addOne: function(note) {
var view = new NotesListView({model: note});
$("#notes-list").append(view.render().el);
},
addAll: function() {
this.model.each(this.addOne);
},
createOnEnter: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val()) return;
this.model.create({title: this.input.val()});
this.input.val('');
},
clearCompleted: function() {
_.invoke(this.model.done(), 'destroy');
return false;
},
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
this.model.each(function (note) { note.save({'done': done}); });
}
});
return NotesView;
});
Solved!
I didn't provide enough information for anyone to find the problem. It was a typo in the element with the ID #new-note. The above code works just fine.
I accomplished loading templates like this by setting the template option in the view like this:
template: _.template(notesTemplate),
and then in my render function calling:
this.$el.html(this.template());
to actually render it. This ensures the events get delegated properly.

Backbone.js and JQueryUI Dialog - events not binding

I'm trying to use Backbone.js to in a JQuery Dialog. I've managed to get the dialog to render and open, but it doesn't seem to be firing my events. I've added a test event to check this, and clicking it doesn't have the expected result.
I've tried following the instructions on this blogpost, regarding delegateEvents, but nothing it made no difference. No errors are thrown, the events just don't fire. Why is this?
Slx.Dialogs.NewBroadcastDialog.View = Backbone.View.extend({
events: {
"click .dialog-content": "clickTest"
},
clickTest : function () {
alert("click");
},
render: function () {
var compiledTemplate = Handlebars.compile(this.template);
var renderedContent = compiledTemplate();
var options = {
title: Slx.User.Language.dialog_title_new_message,
width: 500
};
$(renderedContent).dialog(options);
this.el = $("#newBroadCastContainer");
this.delegateEvents(this.events);
return this;
},
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#newBroadcastDialogTemplate").html();
this.render();
}
});
You might want to try this. I had to refactor your code a bit hope you will get the idea
Slx.Dialogs.NewBroadcastDialog.View = Backbone.View.extend({
el:"#newBroadCastContainer",
template:$("#newBroadcastDialogTemplate").html(),
events: {
"click .dialog-content": "clickTest"
},
clickTest : function () {
alert("click");
},
render: function () {
var compiledTemplate = Handlebars.compile(this.template);
var renderedContent = compiledTemplate();
$(this.el).html(renderedContent).hide().dialog(this.options.dialogConfig);
return this;
},
initialize: function () {
}
});
Instantiate and render outside the View definition
var myDialog = new Slx.Dialogs.NewBroadcastDialog.View({dialogConfig:{title: Slx.User.Language.dialog_title_new_message,width: 500}});
myDialog.render();
The problem turned out to be due to me assigning this.el when I should have been assigning this.$el
This worked perfectly:
Slx.Dialogs.NewBroadcastDialog.View = Backbone.View.extend({
el: "#newBroadcastContainer",
events: {
"click .clicktest": "clickTest"
},
clickTest : function () {
console.log("click");
},
render: function () {
var compiledTemplate = Handlebars.compile(this.template);
var renderedContent = compiledTemplate();
var options = {
title: Slx.User.Language.dialog_title_new_message,
width: 500
};
this.$el = $(renderedContent).dialog(options);
return this;
},
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#newBroadcastDialogTemplate").html();
this.render();
}
});
I had two codebases on one of the code base I was able to bind events by assigning the dialog to this.$el however in the other codebase this somehow did not work. I add the following line this.el = this.$el;
to the code and it is working now. however I am still not able to figure out why it was working in one codebase and not the other and why assigning $el to el got it to work.

Backbone.js fire event on .remove()

I need to fire a function when a Backbone.js view is removed. I guess something like a de-constructor. The code below is a snippet I wrote; I know it won't work. However, I do remember seeing in the past a video tutorial on how to write a function that does this. The reason I need to run a de-constructing function is to clear the interval set inside a view when the view is removed.
ViewWeather = Backbone.View.extend({
interval: setInterval(function() {console.log('interval fire');}, 1000),
// made up function
deconstructor: function () {
// if the view is removed
clearInterval(this.interval);
}
});
var viewweather = new ViewWeather();
This blog post should give you some better info
http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
most notable parts
Backbone.View.prototype.close = function(){
this.remove();
this.unbind();
if (this.onClose){
this.onClose();
}
}
and then
MyView = Backbone.View.extend({
initialize: function(){
this.model.bind("change", this.render, this);
},
render: function(){ ... },
onClose: function(){
this.model.unbind("change", this.render);
}
});
i am not sure if I understand you correctly, but the approach you are showing seems correct:
dispose: function(id) {
clearInterval(this.interval);
this.remove();
}
you are going to have to call dispose yourself, by e.g. an event:
initialize: function(opts) {
router.bind('route:leave', this.dispose, this);
},
edit after comments: this should work to overload remove
remove: function() {
clearInterval(this.interval);
Backbone.View.prototype.remove.call(this);
}

Resources