Backbone error can not call method 'toJSON'? - javascript

I wanna render every waiter from my collection but console still show me error :
Uncaught TypeError: Cannot call method 'toJSON' of undefined
this is my code :
(function() {
window.App = {
Models: {},
Views: {},
Collections: {}
};
window.template = function(id) {
return _.template( $('id' + id).html() );
},
// WAITER MODEL
App.Models.Waiter = Backbone.Model.extend({
defaults: function() {
return {
title: 'Waiter Name',
id: []
};
}
});
// A LIST OF WAITERS COLLECTION
App.Collections.Waiters = Backbone.Collection.extend({
model: App.Models.Waiter
});
// VIEW FOR ALL WAITERS
App.Views.Waiters = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each(function(waiter) {
var waiterView = new App.Views.Waiter({ model: waiter });
this.$el.append(waiterView.render().el);
}, this);
return this;
}
});
// A VIEW FOR ONE PERSON
App.Views.Waiter = Backbone.View.extend({
tagName: 'li',
template: _.template("<%= title %><%= id %>"),
render: function() {
this.$el.html( this.template(this.model.toJSON()) );
return this;
},
});
waitersCollection = new App.Collections.Waiters([
{
title: 'ferko fristansky',
id: 2
},
{
title: 'ferko bandaska',
id: 3
},
{
title: 'fvwerv fristansky',
id: 4
}
]);
var waitersView = new App.Views.Waiter({ collection: waitersCollection });
$(document.body).append(waitersView.render().el);
})();

You're creating your waiterView with a collection:
var waiterView = new App.Views.Waiter({ collection: waitersCollection });
but App.Views.Waiter is a model-based view; that means that this.model will be undefined inside your App.Views.Waiter and so this will fail:
this.$el.html( this.template(this.model.toJSON()) );
// this is undefined -------------^^^^^
You probably want to create an App.Views.Waiters instead:
var waitersView = new App.Views.Waiters({ collection: waitersCollection });
Then, inside App.Views.Waiters, you'd create one App.Views.Waiter for each model in the collection rather than a new App.Views.extend({ model: waiter }):
render: function() {
this.collection.each(function(waiter) {
var waiterView = new App.Views.Waiter({ model: waiter });
this.$el.append(waiterView.render().el);
}, this);
return this;
}
As an aside, be careful with this:
App.Models.Waiter = Backbone.Model.extend({
defaults: {
title: 'Waiter Name',
id: []
}
});
The values from defaults are shallow copied so everything that uses those defaults will end up using exactly the same id array and that can lead to strange bugs when you have several models sharing the same id array. If you have mutable values in defaults, you usually want to use a function instead so that everyone gets their own distinct values:
App.Models.Waiter = Backbone.Model.extend({
defaults: function() {
return {
title: 'Waiter Name',
id: []
};
}
});

Related

How to keep track of actual rendered Model in a Backbone Collection

I'm new to Backbone and I'm trying to create a simple Slideshow that show all the models in a Collection.
Models are created through a fetch from the server and here is the code:
var Post = Backbone.Model.extend({
defaults:{
text: "",
source: "",
image: "",
posted_at: "",
rendered : false,
},
});
In PostCollection there're modelBefore and modelAfter that return next and previous model respectively.
var PostCollection = Backbone.Collection.extend({
model: Post,
url: "https://milkytags.com/api/v1/boards/edcb2c43-1448-4c81-97d5-1c315c8f9589/posts",
initialize: function() {
this.fetch({ data: $.param({ page: pageCounter, per_page:3}) });
},
parse: function(response) {
return response.posts;
},
modelBefore: function(model) {
index = this.indexOf(model) - 1;
if (index < 0) {
index = this.length - 1;
}
return this.at(index);
},
modelAfter: function(model) {
index = this.indexOf(model) + 1;
if (index === this.length) {
index = 0;
}
return this.at(index);
},
});
I've created a view called SlideShowView that creates the view from a template relying on Post View: next and prev method dealing with rendering the next or previous template.
var SlideShowView = Backbone.View.extend({
tagName: 'div',
className: 'slideshow',
events: {
'click #close': 'close',
'click #next': 'next',
'click #prev': 'prev',
},
template: _.template($('#slideShowTemplate').html()),
initialize: function() {
this.render();
},
render: function() {
this.$el.html(this.template());
post = new PostView({ model: this.model });
this.$el.append(post.el);
return this.$el;
},
close: function(){
this.remove();
},
next: function(){
var next = this.model.collection.modelAfter( this.model );
post = new PostView({ model: next });
this.$el.html(this.template());
this.$el.append(post.el);
return this.$el;
},
prev: function(){
var prev= this.model.collection.modelBefore( this.model );
post = new PostView({ model: prev });
this.$el.html(this.template());
this.$el.append(post.el);
return this.$el;
},
});
Finally, Post View:
// The View for single Post
var PostView = Backbone.View.extend({
tagName: 'div',
className: 'post',
events: {
'click' : 'slideShow',
},
template: _.template($('#postTemplate').html()),
initialize: function() {
this.render();
},
render: function() {
this.$el.html(this.template(this.model.toJSON_milky()));
return this;
},
slideShow: function(){
test=new SlideShowView({model: this.model});
$('#milkyContainer').append(test.$el);
}
});
Problems arises when I press next or prev, in practice it is as if the collection was not updated with the latest rendered element, I have to find a way to tell to the collection what is the current collection element shown.
Tips?
Thanks
Right now your code is using this.model in SlideShowView as "the current model". However, you do not update it. Something like this would do it:
next: function(){
var next = this.model.collection.modelAfter( this.model );
post = new PostView({ model: next });
this.$el.html(this.template());
this.$el.append(post.el);
this.model = next; // <<---- Added this line.
return this.$el;
},
Similarly for prev.

Cannot retrieve collection outside of the view

I'm making a simple list of people with option when clicking on person's name the Router will take a name as a parameter 'student/:name' and find a right person's object in a collection. I instantiate collection in a GroupView class by fetching it from the server. And that's where the Error appears: to get the access to collection (so I can find right object) in my viewStudent() method in Router class, I'm making one more instance of GroupView(), and console shows an error and that's right, 'cause there're no objects in collection.
I cannot wrap my head around this, why in GroupView() I receive data from the server and my collection just works fine, but second time I instantiate GroupView() in a Router - there's no collection? Maybe there's any other way I can get access to the collection in my Router? Any help would be greatly appreciated.
var StudentModel = Backbone.Model.extend({
defaults: {
name: 'Volodya',
lastName: 'Peterson',
age: 22,
gender: 'male'
}
});
var StudentsCollection = Backbone.Collection.extend({
model: StudentModel,
url: '/students.json'
});
var StudentView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#studentTpl').html()),
events: {
'click': function () {
eventAggregator.trigger('student:selected', this.model);
}
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var GroupView = Backbone.View.extend({
tagName: 'ul',
initialize: function () {
this.collection = new StudentsCollection();
this.collection.on('update', this.render, this);
this.collection.fetch();
},
render: function () {
var self = this;
this.collection.each(function (student) {
var studentView = new StudentView({
model: student
});
self.$el.append(studentView.render().el);
});
$('body').html(this.$el);
}
});
var RouterView = Backbone.View.extend({
tagName: 'ul',
render: function () {
var self = this;
_.each(this.model.toJSON(), function (value) {
self.$el.append('<li>' + value + '</li>');
});
return this;
}
});
var GroupController = function () {
this.start = function () {
var groupView = new GroupView();
};
};
var Router = Backbone.Router.extend({
routes: {
'': 'index',
'student/:name': 'viewStudent'
},
index: function () {
groupController.start();
},
viewStudent: function (name) {
var groupView = new GroupView();
var selectedStudent = groupView.collection.find(function (student) {
return student.get('name') === name;
});
$('body').append((new RouterView({ model : selectedStudent})).render().el);
}
});
var eventAggregator= _.extend({}, Backbone.Events),
groupController;
$(function () {
var router = new Router();
groupController = new GroupController();
Backbone.history.start();
eventAggregator.on('student:selected', function (student) {
var urlpath= 'student/'+ student.get('name');
router.navigate(urlpath, {trigger: true});
});
});

How to pass filtered data into view from collection in backbone

I know Im pretty close to figuring this out. Im trying to filter out my collection based on if favorite eq true. If I console.log - I can see it's doing its job. But it's not updating my view.
Anyone have any idea what I'm missing or doing wrong?
Here is my code:
var Products = Backbone.Model.extend({
// Set default values.
defaults: {
favorite: false
}
});
var ProductListCollection = Backbone.Collection.extend({
model: Products,
url: '/js/data/wine_list.json',
parse: function(data) {
return data;
},
comparator: function(products) {
return products.get('Vintage');
},
favoritesFilter1: function(favorite) {
return this.filter(function(products) {
return products.get('favorite') == true;
});
},
favoritesFilter: function() {
return this.filter(function(products) {
return products.get('favorite') == true;
});
},
});
var products = new ProductListCollection();
var ProductListItemView = Backbone.View.extend({
el: '#wine-cellar-list',
initialize: function() {
products.bind('reset', this.render, this);
products.fetch();
this.render();
},
render: function() {
console.log(this.collection);
var source = $('#product-template').html();
var template = Handlebars.compile(source);
var html = template(this.collection.toJSON());
this.$el.html(html);
return this;
},
});
// Create instances of the views
var productView = new ProductListItemView({
collection: products
});
var CellarRouter = Backbone.Router.extend({
routes: {
'': 'default',
"favorites": "showFavorites",
"purchased": "showPurchased",
"top-rated": "showTopRated",
},
default: function() {
productView.render();
},
showFavorites: function() {
console.log('Favorites');
productView.initialize(products.favoritesFilter());
},
showPurchased: function() {
console.log('Purchased');
},
showTopRated: function() {
console.log('Top Rated');
}
});
$(function() {
var myCellarRouter = new CellarRouter();
Backbone.history.start();
});
There's many mistakes in your code, I'll try to clarify the most I can :
Your collection should be just like this :
var ProductListCollection = Backbone.Collection.extend({
model: Products,
url: '/js/data/wine_list.json',
comparator: 'Vintage' // I guess you want to sort by this field
});
Your view like this :
var ProductListItemView = Backbone.View.extend({
el: '#wine-cellar-list',
initialize: function() {
this.collection.bind('reset', this.full, this);
this.collection.fetch();
},
full: function() {
this.render(this.collection.models);
},
favorites: function(favorite) {
this.render(this.collection.where(favorite)); // here's the answer to your question
},
render: function(models) {
console.log(models);
var source = $('#product-template').html();
var template = Handlebars.compile(source);
var html = template(models.toJSON()); // You may have to change this line
this.$el.html(html);
return this;
},
});
And in your router :
showFavorites: function() {
console.log('Favorites');
productView.favorites(true); // or false, as you like
}

After Adding a record it is not displaying the data immediately to the view

When I loads the page , it is getting all the datas and I am displaying the datas. But When I add a record, that is I am submitting the form "addcontact", the datas are creating in the database. But It is not adding into the collection and that this.collection.on('add') is not getting triggered. So, I think the problem was because of this. Can any one tell me that where I am doing wrong? Is there any other way to solve this.
This code works functionally, but the only problem with this is , on creating new record using this.collection.create({new post},{wait: true}); the values are getting updated in the database. But it is not adding into the collection.
(function(){
Backbone.emulateHTTP = true;
//Backbone.emulateJSON = true;
window.App = {
Models : {},
Collections: {},
Views : {},
Router : {}
};
window.vent = _.extend({},Backbone.Events);
window.template = function(id){
return _.template( $('#'+id).html() );
};
// Contact Model
App.Models.Contact = Backbone.Model.extend({
validate: function(attrs) {
if( !attrs.first_name ||
!attrs.last_name ||
!attrs.email_address) {
alert('Fill the missing fields');
}
}
});
// Collection
App.Collections.Contacts = Backbone.Collection.extend({
model: App.Models.Contact,
url : 'index.php/ContactsController/contacts'
});
// Global View
App.Views.App = Backbone.View.extend({
initialize: function(){
vent.on('contact:edit',this.editContact,this);
//console.log(this.collection.toJSON());
App.addContactView = new App.Views.AddContact({collection: App.Contacts});
App.allContactsView = new App.Views.Contacts({collection: App.Contacts});
$('#allcontacts').append(App.allContactsView.el);
}
});
// Add Contact View
App.Views.AddContact = Backbone.View.extend({
el: '#addcontact',
initialize: function(){
this.first_name = $('#first_name');
this.last_name = $('#last_name');
this.email_address = $('#email_address');
this.description = $('#description');
//this will fix it
this.collection.on("change", this.render , this);
},
events: {
'submit' : 'addContact'
},
addContact: function(e){
e.preventDefault();
this.collection.create({
first_name: this.first_name.val(), // <===== same as $this.el.find('#first_name')
last_name: this.last_name.val(),
email_address: this.email_address.val(),
description: this.description.val()
},{wait: true});
this.clearForm();
},
clearForm: function(){
this.first_name.val('');
this.last_name.val('');
this.email_address.val('');
this.description.val('');
}
});
// All Contacts Views
App.Views.Contacts = Backbone.View.extend({
tagName: 'tbody',
initialize: function(){
this.collection.on('add',this.addOne,this);
this.render();
},
render: function(){
this.collection.each(this.addOne,this);
//console.log(this.el);
return this;
},
addOne: function(contact){
var ContactView = new App.Views.Contact({model: contact});
//console.log(ContactView.render().el);
this.$el.append(ContactView.render().el);
}
});
// A view for a single View
App.Views.Contact = Backbone.View.extend({
tagName: 'tr',
template: template('allContactsTemplate'),
initialize: function(){
this.model.on('change',this.render,this);
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
})();

Each not working in Backbone view render

I'm just starting out with Backbone and I'm having problems generating a simple html list from some JSON data.
I'm getting the error
Uncaught TypeError: Object function (){return c.apply(this,arguments)} has no method 'each'
Here's my code
var Show = Backbone.Model.extend();
var ShowCollection = Backbone.Collection.extend({
model: Show,
url: 'http://192.168.0.7:8081/api/0b08ecef4eda8c6a28b6be3164a96ac8/?cmd=history&type=downloaded&limit=50',
parse: function(response){
return response.data;
}
});
var ItemView = Backbone.View.extend({
tagName: "li",
template: $("#item").html(),
render: function() {
var templ = _.template(this.template);
this.$el.html(templ(this.model.toJSON()));
return this;
}
});
var ShowView = Backbone.View.extend({
el: $("#history"),
initialize: function() {
this.collection = ShowCollection;
this.render();
},
render: function() {
this.collection.each(function(item) {
this.renderItem(item);
}, this);
},
renderItem: function(item) {
var itemView = new ItemView({ model: item });
this.$el.append(itemView.render().el);
}
});
var history = new ShowView();
Here's my data
{
data: [
{
date: "2013-03-16 05:14",
episode: 10,
provider: "-1",
quality: "HD TV",
resource: "bering.sea.gold.s02e10.720p.hdtv.x264-bajskorv.mkv",
resource_path: "/Users/Machine/Tv/Bering.Sea.Gold.S02E10.720p.HDTV.x264-BAJSKORV repost",
season: 2,
show_name: "Bering Sea Gold",
status: "Downloaded",
tvdbid: 254203
}
],
message: "",
result: "success"
}
this.collection = ShowCollection;
should be
this.collection = new ShowCollection();
You're assigning your this.collection to the extended Backbone Collection class, rather than to an instance of it. See the docs on extend for an example. You should have something like this.collection = new ShowCollection().

Categories