Issue with el backbone view - backbone.js

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));

Related

backbonejs model.toJSON & render

I have some model and I want to bind render method to it on change. I'm trying to pass the model.toJSON to the render, but it doesn't work. However it works if I pass model and apply toJSON inside render.
(the whole code is here: http://plnkr.co/edit/xoeY4hexnqgHnkxap5uj?p=preview)
window.onload=function(){
var defaultModel = Backbone.Model.extend({
defaults: {
greeting: 'Hello, Dude',
content: 'Coming soon...'
}
}),
defaultView = Backbone.View.extend({
tagName: 'section',
className: 'default',
initialize: function(option) {
this.template = $('#tmpl-default').html();
this.render();
var _this = this;
this.model.bind('change', _.bind(this.render, this, this.model.toJSON()));
$('[name="default-input"]').on('blur', function() {
console.log('got blurred....');
_this.model.set('content', this.value);
});
},
render: function(content) {
if (!content) {
console.log('%cno content', 'color: green');
content = this.model.toJSON();
}
this.$el.html(_.template(this.template)(content));
$('#content').html(this.$el);
return this;
}
}),
viewDefault = new defaultView({
model: new defaultModel()
});
};
the code above doesn't work. If I change
this.model.bind('change', _.bind(this.render, this, this.model.toJSON()));
to
this.model.bind('change', _.bind(this.render, this, this.model));
and
if (!content) {
content = this.model.toJSON();
}
to
if (!content) {
content = this.model.toJSON();
}else{
content = content.toJSON();
}
But why?!
A more appropriate way is to use the listenTo function on the view, such as:
this.listenTo(this.model, "change", this.render);
I think the reason it doesn't work as you expect is because when you do
this.model.bind('change', _.bind(this.render, this, this.model.toJSON()));
The argument this.model.toJSON() passed to render method will always be the initial state of the model at the point when _.bind was called.
When you do content = this.model.toJSON(); inside render method, you get the current state, including the expected changes that triggered render.
You can better structure your view like this:
defaultView = Backbone.View.extend({
tagName: 'section',
className: 'default',
initialize: function(option) {
this.render();
this.model.on('change', _.bind(this.render, this));
},
template: _.template($('#tmpl-default').html()),
events: {
'blur [name="default-input"]': 'eventHandler'
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
$('#content').html(this.$el);
return this;
},
eventHandler: function(event) {
var val = $(event.currentTarget).val();
this.model.set('content', val);
}
});
Also, look into listenTo than on like #Jayem suggested to aviod unexpected memory leak issues

Calling destroy on collection

I am doing a sample application similar to the Backbone-Todo. But when I am invoking destroy on collection it's giving error:
Uncaught TypeError: Cannot read property 'destroy' of undefined
How can I solve this problem. Please suggest.
Following is my method code:
$(function(){
var Todo = Backbone.Model.extend({
defaults: function() {
return {
title: "empty todo...",
order: Todos.nextOrder(),
done: false
};
}
});
var TodoList = Backbone.Collection.extend({
model : Todo,
localStorage: new Backbone.LocalStorage("todos-backbone"),
done: function() {
return this.where({done: true});
},
remaining: function() {
return this.without.apply(this, this.done());
},
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
comparator: 'order'
});
var TodoView = Backbone.View.extend({
tagName: "li",
template: _.template($('#item-template').html()),
events: {
"click a.destroy" : "clear"
},
initialize: function() {
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
clear: function(){
this.model.destroy();
}
});
var AppView = Backbone.View.extend({
el: $("#todoapp"),
statsTemplate: _.template($('#stats-template').html()),
events: {
"keypress #new-todo": "createOnEnter",
"click #remove-all": "clearCompleted"
},
initialize: function() {
this.input = this.$("#new-todo");
this.main = $('#main');
this.footer = this.$('footer');
this.listenTo(Todos, 'add', this.addOne);
this.listenTo(Todos, 'all', this.render);
Todos.fetch();
},
render: function() {
var done = Todos.done().length;
var remaining = Todos.remaining().length;
if (Todos.length) {
this.main.show();
this.footer.show();
this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
},
createOnEnter: function(e){
if(e.keyCode != 13) return;
if (!this.input.val()) return;
Todos.create({
title: this.input.val()
})
this.input.val('');
},
addOne: function(todo){
var view = new TodoView({model: todo});
this.$("#todo-list").append(view.render().el);
},
clearCompleted: function(){
_.invoke(Todos, 'destroy');
return false;
}
});
for this answer I assume Todos is an instance of TodoList. I also assume that your error is fired by this function in your AppView
clearCompleted: function(){
_.invoke(Todos, 'destroy');
return false;
}
In there you're trying to treat your Backbone.js Collection instance like what it is, a collection eg a list. But Backbone collections are not simply lists, they are objects that have the property models which is a list that contains all your models. So trying to use underscore's invoke (which works on lists) on an object is bound to cause errors.
But don't worry, Backbone neatly implements many Underscore methods for its Model and Collection, including invoke. This means you can invoke destroy for each model in a collection like this
SomeCollection.invoke('destroy');
Hope this helps!

Backbone listen to event from Raphael freeTransform

I know how to handle general events, like click etc., but I am having a problem to understand how to listen to events fired by the function in my render function:
function( ft, events) {
console.log(events);
}); /*fires ["drag start"]
["drag"]
["apply"]
["drag"]
["apply"]
["drag"]
etc.*/
Now I would like to have handlers in my view that listen to these events. How would I do this?
This is the entire View:
var myView = Backbone.View.extend({
initialize: function () {
self = this;
this.element = paper.rect();
this.setElement(this.element.node);
this.delegateEvents(this.events);
},
events: {
"click": "showHandles",
"drag end": "dragEndHandler"
},
dragEndHandler: function(e){
console.log('dragEnd');
},
showHandles: function(e){
this.ft.showHandles();
},
render: function(){
this.element.attr({
'x': this.model.get('x'),
'y': this.model.get('y'),
'width': this.model.get('width'),
'height': this.model.get('height'),
'fill': this.model.get('fill'),
'cursor': this.model.get('cursor')
})
// Add freeTransform with options and callback
this.ft = paper.freeTransform(this.element, {
'keepRatio': ['axisX', 'axisY'],
'size': 4,
//set handle
'attrs': {'fill': '#436eee', 'stroke': '#fff'}
},
function( ft, events) {
console.log(events);/*fires ["drag start"]
["drag"]
["apply"]
["drag"]
["apply"]
["drag"]
etc.*/
});
return this;
}
});
FreeTransform accepts a callback as a third argument, you can set it to a function bound to your view :
var myView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'transformed');
// ...
},
transformed: function (ft, events) {
console.log(this);
console.log(events);
},
render: function (){
// ...
this.ft = paper.freeTransform(this.element, options, this.transformed);
return this;
}
// ...
});
view.transformed will be called with this set to the view thanks to _.bindAll
To react to specific events, you will have to check if the events array contains the associated value:
transformed: function (ft, events) {
if (_.indexOf(events, "scale end")!==-1) {
console.log("do something with scale end");
}
},
See http://jsfiddle.net/nikoshr/BC2X3/ for a demo
Or you could build your own event routing method:
transformed: function (ft, events) {
var i = 0, l = events.length, ev;
for (; i<l; i++) {
ev = 'transformed_' + events[i].replace(' ', '_');
if (this[ev])
this[ev](ft);
}
},
transformed_scale_end: function() {
console.log("do something with scale end");
},
transformed_rotate_end: function() {
console.log("do something with rotate end");
},
See http://jsfiddle.net/nikoshr/BC2X3/1/ for an updated demo

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.

Call method in collection, when View's method called

I have a view and collection like this:
window.DmnView = Backbone.View.extend({
template: _.template($("#tmpl_dmnListItem").html()),
events: {
"click .getWhois": "showWhois",
"click .getDomain": "toBasket"
},
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
},
render: function() {
return $(this.el)
.attr("class", this.model.get("free") ? "dmnItem green" : this.model.get("checked") ? "dmnItem red" : "dmnItem red loader")
.html(this.template(this.model.toJSON()));
},
remove: function() {
$(this.el).remove();
},
showWhois: function() {
showBoxes(this.model.get("info"));
return false;
},
toBasket: function() {
this.model.toBasket();
console.log("view");
}
});
window.DmnListApp = Backbone.View.extend({
el: $("#regWrap"),
events: {
"keypress #dmnName": "checkAll"
},
initialize: function() {
this.input = this.$("#dmnName");
this.list = this.$("#dmnList");
this.basket = this.$("#dmnBasket");
dmnList.bind('add', this.addOne, this);
dmnList.bind('all', this.render, this);
DmnView.bind('toBasket', this.toBasket, this);
},
render: function() {
},
addOne: function(dmnItem) {
var view = new DmnView({model : dmnItem});
this.list.append(view.render());
},
checkOne: function(name, zone, price, days) {
dmnList.create({name: name, zone: zone, price: price, days: days});
},
checkAll: function(e) {
var name = this.input.val();
if (!name || e.keyCode != 13) return;
if (name == "")
name = "yandex";
dmnList.destroyAll();
var zoneList = dmnList.domainsInfo.Name;
var costList = dmnList.domainsInfo.CostOrder;
var daysList = dmnList.domainsInfo.DaysToProlong;
var parent = this;
$.each(zoneList, function(key, zone) {
parent.checkOne(name, zone, costList[key], daysList[key]);
});
this.input.val("");
},
toBasket: function(){
console.log("collection");
}
});
I want Collection's method toBasket() to be called after View's method toBasket() was called. For this purpose I do the following in Collection:
DmnView.bind('toBasket', this.toBasket, this);
So, if this worked, I should receive two messages in my javascript console:
view
collection
(Maybe in other order)
But I only see "view" message in console. What I do wrong?
TIA!
You're almost there. In your collection view, you're attempting to listen to the DmnView event toBasket, but how you have it setup is a little incorrect. To listen to events, you have to bind to a specific instance you want to listen to, not a class. So you'll want to move the bind from initialize to addOne, like this:
window.DmnListApp = Backbone.View.extend({
// ...
initialize: function() {
this.input = this.$("#dmnName");
this.list = this.$("#dmnList");
this.basket = this.$("#dmnBasket");
dmnList.bind('add', this.addOne, this);
dmnList.bind('all', this.render, this);
// Remove the DmnView bind here
},
addOne: function(dmnItem) {
var view = new DmnView({model : dmnItem});
// Bind to the DmnView instance here
view.bind('toBasket', this.toBasket, this);
this.list.append(view.render());
},
// ...
});
Now that your collection view is listening for the event toBasket, you need to actually fire the event in your DmnView view.
In Backbone views, no events are automatically fired, so you'll need to manually trigger it yourself, like this:
window.DmnView = Backbone.View.extend({
// ...
toBasket: function() {
this.model.toBasket();
console.log("view");
// Trigger the event
this.trigger('toBasket');
}
});
You should now see both messages in your console.

Resources