How to call view from the view in backbone.js - backbone.js

I have two view files views.js and views2.js .On button click views.js call.On views.js there is a button that needs to triggers views2.js.But the problem is that on button click views2.js
is not working.
I have written some code.
In views.js
define( ["backbone"], function(Backbone) {
var UserView = Backbone.View.extend({
tagName: "div",
render: function(){
this.$el.html(this.model.get("desc") + "<button id='btn2' class='btn btn- primary'>Next Page</button>");
return this;
}
});
var NextPageView = Backbone.View.extend({
events: {
'click': 'firstquestion'
},
render: function(){
return this;
},
firstquestion: function(){
//to solve circular dependency
require(['routes'],function(routes){
var router = routes.pageRouter;
router.navigate('view', {trigger: true});
});
}
});
var views2 = require("views2");//should I call views2 here
var np2 = new views2.NextPageView2({el: $("#btn2")});
return {
UserView: UserView,
NextPageView: NextPageView
};
});
In views2.js
define( ["backbone"], function(Backbone) {
var UserView2 = Backbone.View.extend({
tagName: "div",
render: function(){
this.$el.html(this.model.get("desc") + "<button id='btn3' class='btn btn-primary'>Next Page</button>");
return this;
}
});
var NextPageView2 = Backbone.View.extend({
events: {
'click': 'secondquestion'
},
render: function(){
return this;
},
secondquestion: function(){
require(['routes2'],function(routes2){
var router = routes2.pageRouter2;
router.navigate('view', {trigger: true});
});
}
});
return {
UserView2: UserView2,
NextPageView2: NextPageView2
};
});

You need to put a Require.js dependency on the calling view.

Related

Backboe memory leak in subviews

I still strugling with memory leak in my app. I wannted to do it without huge changes in code.
var ItemsView = Backbone.View.extend({
id:'products', // If I change it to el: document.getElementById('products') and without passing views into items object, my views are properly rendered but with memory leak
events: { },
initialize: function() {
_.bindAll(this);
this.listenTo(this.collection, 'reset', this.reset);
this.listenTo(this.collection, 'add', this.addItem);
this.listenTo(this.collection, 'change', this.changeItem);
this.listenTo(this.collection, 'destroy', this.delItem);
this.items = [];
},
reset: function(){ console.log("reset");
this.el.innerHTML = null;
this.render();
},
render: function(){
for(var i=0; i < this.collection.length; i++){
this.renderItem(this.collection.models[i]);
}
},
renderItem: function( model ){
var itemView = new ItemView({ model: model });
itemView.render();
this.items.push(itemView);
jBone(this.el).append(itemView.el);
},
addItem: function(){ console.log("addItem");
this.renderItem();
},
changeItem: function(){ console.log("changeItem"); },
delItem: function(){ console.log("delItem"); },
remove: function() {
_.invoke(this.items, 'remove');
this.items = [];
Backbone.View.prototype.remove.call(this);
}
});
return ItemsView;
This is my Itemsview it is executed when user hit orderview, there is created ItemView for every model in collection:
var ItemView = Backbone.View.extend({
tagName: "li",
className: "productcc",
initialize: function () {
_.bindAll(this, 'addItem', 'removeItem', 'updateItem');
this.listenTo(this.model, 'remove', this.removeItem);
this.listenTo(this.model, 'change', this.updateItem);
},
events: {},
render: function () {
var model = this.model.toJSON();
this.el.innerHTML += '<div class="tabody"><h4 class="tablename">'+model.title+'<h4>'+model.status+'</div>';
return this;
},
addItem: function(){
this.collection.create({"table_no":"demo"});
},
changeItem: function(e){
e.preventDefault();
this.model.save({ table_no: 'demo' });
},
updateItem: function(newstuff){
console.log("updateItem");
console.log(this.el);
},
delItem: function(){
this.model.destroy({ silent: true });
},
removeItem: function(model){
console.log("removeItem");
console.log(model);
var self = this;
self.el.remove();
}
});
return ItemView;
MY ROUTER:
var AppRouter = Backbone.Router.extend({
routes: {
'' : 'home',
'home' : 'home',
'customer/:customer_id': 'showItems'
}
});
var initialize = function(options) {
window.app_router = new AppRouter;
window.socket = io.connect('www.example.com');
this.socketOrdersCollection = new SocketOrdersCollection();
this.ordersView = new OrdersView({ collection: this.socketOrdersCollection });
this.socketOrdersCollection.fetch({ reset: true });
app_router.on('route:home', function() { });
app_router.on('route:showItems', function(customer_id) {
if (this.itemsView) {
this.itemsView.remove();
}
this.socketItemsCollection = new SocketItemsCollection();
this.socketItemsCollection.fetch({ data: { id: customer_id}, reset: true });
this.itemsView = new ItemsView({
collection: this.socketItemsCollection,
model: { tableName: customer_id }
});
});
Backbone.history.start();
};
I have to remove also ItemsView after click to another order...
Thanks for any opinion.
Ok. Let me take a stab at what you're attempting here.
var ItemsView = Backbone.View.extend({
el: document.getElementById('products'),
events: { },
initialize: function() {
// everything you had before
this.items = [];
},
// etc.
renderItem: function( model ){
var itemView = new ItemView({ model: model });
itemView.render();
this.items.push(itemView);
jBone(this.el).append(itemView.el);
},
// etc.
// we're overloading the view's remove method, so we clean up our subviews
remove: function() {
_.invoke(this.items, 'remove');
this.items = [];
Backbone.View.prototype.remove.call(this);
}
});
return ItemsView;
And then in the router:
var initialize = function(options) {
// etc.
app_router.on('route:home', function() { });
app_router.on('route:showItems', function(customer_id) {
if (this.itemsView) {
this.itemsView.remove();
}
// everything else the same
});
Backbone.history.start();
};
So now, your ItemsView will clean up any child items it has, and when you change customers, you'll clean up any ItemsView you have open before generating a new one.
EDIT
I see what you're having a problem with now.
In your route handler, you're going to need to do something along these lines:
app_router.on('route:showItems', function(customer_id) {
// everything you already have
jBone(document.getElementById('container')).append(this.itemsView);
});

backbone fetch collection on infinite scroll

I'm learning backbone and now thinking of how to apply an infinite scroll and fetch/load lets say 20 items from my collection every time the scroll is at the bottom of the page.
I have been searching around a lot after different libs and such without really getting any closer. Anyone that can explain/show how this is best done?
I have now added the infiniscroll.js plugin and trying to get it to work. But on scroll it won't load new items. What am i supposed to do on the appendRender? and how?
var StartView = Backbone.View.extend({
tagName: "section",
id: "start",
className: "content",
events: {
},
initialize: function(){
$(".container").html(this.el);
console.log("init start");
this.template = _.template($("#start_template").html(), {} );
this.collection = new VideoCollection();
_.bindAll(this, "render");
this.render();
this.infiniScroll = new Backbone.InfiniScroll(this.collection, {
success: this.appendRender,
pageSize: this.collection.length,
scrollOffset: 100
});
},
appendRender: function() {
var self = this;
self.$el.html(self.template);
self.$el.find(".videos").append("<div style='margin-bottom:30px; width:100%; height:170px; float:left; background-color:#e4e4e4;'>fff</div>")
},
render: function(){
var self = this;
this.$el.html("loading");
console.log("render start")
},
kill: function() {
console.log("kill start");
this.remove();
}
});
return StartView;
The backbone-pageable plugin supports infinite scrolling.
It's just a matter of your collection extending Backbone.PageableCollection, and you specifying some extra properties. There's also an example of a backbone view listening to the changing collection, as well as fetching on scroll.
It's all described on the github page. It's updated fairly often.
I would have done it something like this (although the document.addEventListener('scroll')-part isn't really elegant
(function() {
"use strict";
var Item = Backbone.Model.extend({});
var Items = Backbone.Collection.extend({
model: Item,
url: "/api/items/"
});
var ItemView = Backbone.View.extend({
tagName: "li",
render: function() {
this.$el.html(this.model.get("name"));
return this;
}
});
var ItemsList = Backbone.View.extend({
tagName: "ul",
offset: 0,
limit: 60,
initialize: function() {
this.collection = new Items();
this.collection.on("reset", this.addAll, this);
this.collection.on("add", this.addOne, this);
this.getItems();
},
render: function () {
return this;
},
getItems: function () {
this.collection.fetch({
"data": {"offset": this.offset, "limit": this.limit},
"success": _.bind(function(e){
this.offset += this.limit;
}, this)
});
},
addOne: function(item) {
var view = new ItemView({model: item});
this.$el.append(view.render().$el);
},
addAll: function() {
this.collection.each(this.addOne, this);
}
});
var itemsList = new ItemsList();
$(document.body).append(itemsList.render().$el);
document.addEventListener('scroll', function (event) {
if (document.body.scrollHeight == document.body.scrollTop + window.innerHeight) {
itemsList.getItems();
}
});
}());

BackboneJS - Cannot call method 'on' of undefined

I have this simple BackboneJS app and it keeps returning this error on adding new model to collection: Cannot call method 'on' of undefined. Can someone help me. I can't see the problem in here.I have my templates defined in index.html, and I am using Slim framework and NotORM.
(function(){
window.App =
{
Models:{},
Collections: {},
Views : {}
}
window.template = function(id)
{
return _.template( jQuery('#' + id).html());
}
App.Models.Party = Backbone.Model.extend({
});
App.Collections.Partys = Backbone.Collection.extend({
model: App.Models.Party,
url: "http://localhost/BackboneJS/vjezba6/server/index.php/task"
});
App.Views.Party = Backbone.View.extend({
tagName :"div",
className: "box shadow aktivan",
template: template("boxovi"),
initialize: function()
{
this.model.on('change', this.render, this);
},
events:{
"click .izbrisi" : "izbrisi"
},
render: function()
{
var template = this.template( this.model.toJSON() );
this.$el.html(template);
return this;
},
izbrisi: function()
{
this.model.destroy();
},
ukloni: function()
{
this.remove();
}
});
App.Views.Partys = Backbone.View.extend({
tagName:"div",
id: "nosac-boxova",
initialize: function()
{
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(party) {
var partyView = new App.Views.Party({ model: party });
this.$el.append(partyView.render().el);
}
});
App.Views.Dodaj = Backbone.View.extend({
tagName: "div",
template : template("dodajTemp"),
events:
{
'submit' : 'submit'
},
submit : function(e)
{
e.preventDefault();
console.log(e);
var nazivPolje = $(e.currentTarget).find(".naziv").val();
var tekstPolje = $(e.currentTarget).find(".lokal").val();
var noviParty = new App.Views.Party({naziv:nazivPolje, tekst: tekstPolje});
this.collection.create(noviParty);
},
initialize: function()
{
},
render: function()
{
var template = this.template();
this.$el.html(template);
return this;
}
});
/* var kolekcijaPartya = new App.Collections.Partys([
{
naziv:"Ovo je prvi naziv",
tekst: "Ovo je prvi tekst"
},
{
naziv:"Ovo je drugi naziv",
tekst: "Ovo je drugi tekst"
}
]);*/
var kolekcijaPartya = new App.Collections.Partys;
kolekcijaPartya.fetch({
success: function()
{
var partysView = new App.Views.Partys({collection:kolekcijaPartya});
$("#content").prepend(partysView.render().el);
$("div.box").slideAj();
var dodajView = new App.Views.Dodaj({collection: kolekcijaPartya});
$("div#sidebar-right").html(dodajView.render().el);
}
});
})();
var noviParty = new App.Views.Party({naziv:nazivPolje, tekst: tekstPolje});
this.collection.create(noviParty);
so you are trying to add a View to your collection?

backbone.js events not firing after re-render even with delegateEvents()

I have a View whit nested views inside that when rendered has a link on it with a click event attached. First time I click on al link, all works. But when re-render all views, links events are disappeared. I tried forcing delegateEvents() in render and remove views before re-render but nothing.
Here is my code:
var SlideView = Backbone.View.extend({
tagName: 'li',
events: {
'click .nested':'destroy'
},
template: _.template($('#slides-nested-template').html()),
render: function(e){
var _el = $(this.el);
_el.html(this.template(this.model.toJSON()));
this.delegateEvents(this.events);
return this;
},
destroy: function(e){
e.preventDefault();
Presentations.eliminate($(e.target).attr('rel'));
}
});
var SectionView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#slides-template').html()),
events: {
'click .no-nested':'destroy'
},
initialize: function(options){
this.views = [];
_.bindAll(this, 'destroy');
},
render: function(){
var _el = $(this.el), self=this;
_el.html(this.template(this.model.toJSON()));
_ul = this.$('ul');
var data = Presentations.filter(function(slide){
return slide.get('padre') == self.model.get('id');
});
_.each(data, function(slide){
view = new SlideView({
model: slide
});
self.views.push(view);
_ul.append(view.render().el);
});
this.delegateEvents(this.events);
return this;
},
destroy: function(e){
e.preventDefault();
Presentations.eliminate($(e.target).attr('rel'));
},
removeViews: function(){
while (this.views.length){
var view = this.views.shift();
view.undelegateEvents();
view.remove();
view = null;
}
}
});
var SectionList = Backbone.View.extend({
tagName: 'ul',
className: 'sortable',
initialize: function(options){
Presentations.on('add', this.render, this);
Presentations.on('reset', this.render, this);
Presentations.on('remove', this.render, this);
this.views = [];
},
render: function(e){
//this.removeViews();
$('#slides .content').empty();
var data = Presentations.filter(function(slide){
return slide.get('padre') === false;
});
var view, self=this, _el=$(this.el);
_.each(data, function(slide){
view = new SectionView({
model: slide
});
_el.append(view.render().el);
self.views.push(view);
});
$('#slides .content').append(_el);
return this;
},
removeViews: function(){
while (this.views.length){
var view = this.views.shift();
view.undelegateEvents();
view.removeViews();
view.remove();
view = null;
}
}
});
var Presentation = Backbone.Model.extend({
});
var PresentationList = Backbone.Collection.extend({
model: Presentation,
url: BASE_URL+'api/slides/'+PRESENTATION,
eliminate: function(id){
this.each(function(slide){
if (slide.get('padre')==id){
slide.set('padre', false);
slide.save();
}
});
var ref = this.get(id);
ref.destroy();
this.remove(ref);
}
});
Presentations = new PresentationList();
SectionListView = new SectionList();
Presentations.fetch();

Backbone render placement

Just wondering why this works:
window.NewListView = Backbone.View.extend({
template: _.template('Create New List'),
render: function(){
$(this.el).html(this.template());
return this;
}
});
window.List = new (Backbone.Router.extend({
routes: { "": "index" },
initialize: function(){
this.newListView = new NewListView();
},
start: function(){
Backbone.history.start();
},
index: function(){
$('.lists').append(this.newListView.render().el);
}
}));
$(function(){ List.start(); })
And this doesn't:
window.NewListView = Backbone.View.extend({
template: _.template('Create New List'),
render: function(){
$(this.el).html(this.template());
return this;
}
});
window.List = new (Backbone.Router.extend({
routes: { "": "index" },
initialize: function(){
this.newListView = new NewListView();
$('.lists').append(this.newListView.render().el);
},
start: function(){
Backbone.history.start();
},
index: function(){
}
}));
$(function(){ List.start(); })
The difference is just moving
$('.lists').append(this.newListView.render().el);
between initialize() and index() of the router.
Its because of the way, and when, you're creating an instance of your router.
When you do:
window.List = new (Backbone.Router.extend({...
You're creating an instance of the router before the DOM is loaded. Therefore, in your initialize function, your jQuery selector is not returning any nodes.
If you open up a console, you can see the order of operations logged to it on this jsFiddle:
http://jsfiddle.net/edwardmsmith/x64hw/
window.NewListView = Backbone.View.extend({
template: _.template('Create New List'),
render: function(){
$(this.el).html(this.template());
return this;
}
});
window.List = new (Backbone.Router.extend({
routes: { "": "index" },
initialize: function(){
this.newListView = new NewListView();
console.log("List Initialize");
$('.lists').append(this.newListView.render().el);
},
start: function(){
Backbone.history.start();
},
index: function(){
}
}));
$(function(){
console.log("Before List Start");
List.start();
console.log("After List Start");
})​
Results in:
List Initialize
Before List Start
After List Start
But, if you create your router instance after the DOM loads:
window.NewListView = Backbone.View.extend({
template: _.template('Create New List'),
render: function(){
$(this.el).html(this.template());
return this;
}
});
window.List = Backbone.Router.extend({
routes: { "": "index" },
initialize: function(){
this.newListView = new NewListView();
console.log("List Initialize");
$('.lists').append(this.newListView.render().el);
},
start: function(){
Backbone.history.start();
},
index: function(){
}
});
$(function(){
console.log("Before List Start");
list = new List();
list.start();
console.log("After List Start");
})​
The order is as you would expect, and it works:
Before List Start
List Initialize
After List Start
as shown in this jsFiddle:
http://jsfiddle.net/edwardmsmith/eDWfh/

Resources