Backbone.js Collection's fetch call is erroring out - javascript

Still wrapping my head around Backbone, and running into an issue where my Collection won't populate when I'm passing it JSON from a file. The fetch() call is erroring out, but I'm not sure how to debug it exactly, as my console.log()'s aren't telling me anything useful (as far as I know how to dig through them).
Here's the code:
ItemGroup = Backbone.Collection.extend({
model: Item, // left out definition since it's very simple
url: 'js/items.json',
initialize: function(models, options) {
this._meta = {};
},
parse: function(response) { // parse is not being called, not sure why?
this.set(response.name, 'name');
return response.items;
},
set: function(val, prop) {
this._meta[prop] = val;
},
get: function(prop) {
return this._meta[prop];
}
});
ItemGroupView = Backbone.View.extend({
el: '#item-container',
initialize: function(options) {
this.collection.bind('reset', this.render, this);
this.collection.fetch({
success : function() {
alert('successful');
},
error : function(collection, xhr, options) { // This is being triggered, not sure why?
console.log(collection);
console.log(xhr);
console.log(options);
}
});
},
processItem: function(item) {
var itemView = new ItemView({ model: item });
this.$el.append(itemView.render().el);
},
render: function() {
console.log(this.collection); // this shows an empty collection
var itemGroupHtml = this.template({ name: this.collection.get('name') });
$(this.main).html(itemGroupHtml);
_.each(this.collection.models, this.processItem, this);
return this;
}
});
var toiletryItems = new ItemGroup();
var toiletryGroupView = new ItemGroupView({ collection: toiletryItems });
And here's my json (I can see that's it's successfully finding the file, since I see it as a 200 in the network requests using the Chome inspector):
[
{
'name' : 'toiletries',
'items' : [
{ 'name': 'toothbrush' },
{ 'name': 'deodorant' },
{ 'name': 'toothpaste' },
]
}
]
Thanks in advance!
UPDATE:
Fixed the invalid json, but still seeing an empty toiletryItems collection. Any ideas?

Discovered the problem was with my parse function. The response param is an array, and so that function should be:
parse: function(response) { // parse is not being called, not sure why?
this.set(response[0].name, 'name');
return response[0].items;
},

Related

Backbone Fetch success callback not executing when fetch response is an empty array

I have Person model and I am retrieving a person info inside a view. The success callback FetchSuccess executes when the response has an object. But when response is empty, the callback is not called. Any Guess?
Models.Basic = Backbone.Model.extend({
parse: function(response) {
return response;
}
});
Models.PersonModel = Backbone.Model.extend({
url: function() {
return '/person/' + this.data.id;
}
});
Backbone.View.extend({
template: Templates['template'],
initialize: function(options) {
this.id = options.id;
_.bindAll(this, 'FetchSuccess');
this.personModel = new Models.PersonModel();
this.model = new Models.Basic();
this.fetchData();
return this;
},
render: function() {
this.$el.append(this.template(this.model.toJSON()));
},
fetchData: function() {
this.personModel.data = {
id: this.id
};
this.personModel.fetch({
context: this,
success: this.FetchSuccess
});
},
FetchSuccess: function() {
this.model.set({
name: this.personModel.get('name');
});
this.render();
}
});
this.personModel = new Models.PersonModel();
This is a Backbone Model, not a collection.
this.personModel.fetch({
reset: true, // this doesn't exist on model
success: this.FetchSuccess
});
You can't fetch a model without an id. Also, the model, when fetching, expects an object to be returned.
If you want to fetch a specific person, give an id to the model, then fetch.
this.personModel = new Models.PersonModel({ id: "id_here" });
// ...
this.personModel.fetch({
context: this,
success: this.FetchSuccess
});
Here's the code with the corrections
// parse isn't needed if you're not going to parse something
Models.Basic = Backbone.Model.extend({});
Models.PersonModel = Backbone.Model.extend({
urlRoot: 'person/', // this handles putting the id automatically
});
Backbone.View.extend({
template: Templates['template'],
initialize: function(options) {
this.id = options.id;
// pass the id here
this.personModel = new Models.PersonModel({ id: this.id });
this.model = new Models.Basic();
this.fetchData();
// makes no sense in the initialize since it's never called
// manually and never used to chain calls.
// return this;
},
render: function() {
// render should be idempotent, so emptying before appending
// is a good pattern.
this.$el.html(this.template(this.model.toJSON()));
return this; // this is where chaining could happen
},
fetchData: function() {
// This makes no sense unless you've stripped the part that uses it.
// this.personModel.data...
this.personModel.fetch({
context: this, // pass the context, avoid `_.bindAll`
success: this.onFetchSuccess,
error: this.onFetchError
});
},
onFetchSuccess: function() {
this.model.set({
name: this.personModel.get('name')
});
this.render();
},
onFetchError: function() { this.render(); }
});
You could catch the error with the error callback, or just do nothing and render by default, and re-render on fetch.
You could also listen to the model events (inside the initialize):
this.listenTo(this.personModel, {
'sync': this.FetchSuccess,
'error': this.onFetchError
});
this.personModel.fetch();

Wrong backbone collection length. Can't each this collection

Sorry for my bad English. Tell me why the following happens:
I have some backbone collection:
var Background = window.Models.Background = Backbone.Model.extend({});
var Backgrounds = window.Models.Backgrounds = Backbone.Collection.extend({
model: window.Models.Background,
url: '/backgrounds/',
initialize: function() {
this.fetch({
success: this.fetchSuccess(this),
error: this.fetchError
});
},
fetchSuccess: function( collect_model ) {
new BackgroundsView ({ collection : collect_model });
},
fetchError: function() {
throw new Error("Error fetching backgrounds");
}
});
And some view:
var BackgroundsView = window.Views.BackgroundsView = Backbone.View.extend({
tagName: 'div',
className: 'hor_slider',
initialize: function() {
this.render();
},
render: function() {
console.log(this.collection);
this.collection.each( function (background) {
console.log(background);
//var backgroundView = new BackgroundView ({ model: background });
//this.$el.append(backgroundView.render().el);
});
}
});
now i creating collection
var backgrounds = new Models.Backgrounds();
but when I must render this view, in the process of sorting the collection its length is 0, but should be two. This log I see at console. How is this possible? What am I doing wrong??
You are creating the view before the collection fetch is successfull. Your code should be:
initialize: function() {
this.fetch({
success: this.fetchSuccess,
//------------------------^ do not invoke manually
error: this.fetchError
});
},
fetchSuccess: function(collection, response) {
new BackgroundsView ({ collection : collection});
},
You should let backbone call fetchSuccess when the fetch succeeds. Right now you're invoking the funcion immediately and passing the return value undefined as success callback.
This looks like a wrong pattern. Your data models shouldn't be aware of/controlling the presentation logic.
You have a view floating around without any reference to it. You should be creating a view instance with reference(for example from a router, or whatever is kick starting your application) and passing the collection to it. Then fetch the collection from it's initialize method and render after the fetch succeeds. Collection can be referenced via this.collection inside view.
Alternatively you can fetch the collection from router itself and then create view instance. Either way collection/model shouldn't be controlling views.
If the code is structured in the following way, the problem is solved. It was necessary to add a parameter reset to fetch.
var Background = window.Models.Background = Backbone.Model.extend({});
var Backgrounds = window.Models.Backgrounds = Backbone.Collection.extend({
model: window.Models.Background,
url: '/backgrounds/',
initialize: function() {
this.fetch({
reset : true,
});
}
});
var BackgroundsView = window.Views.BackgroundsView = Backbone.View.extend({
tagName: 'div',
className: 'hor_slider',
initialize: function() {
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.collection.each( function (background) {
var backgroundView = new BackgroundView ({ model: background });
this.$el.append(backgroundView.render().el);
}, this);
$('#view_list').empty();
$('#view_list').append(this.$el);
return this;
}
});

Backbone - model.destroy() function not defined for model

For some reason, I am getting a TypeError in my JavaScript regarding a supposed Backbone model object for which I am trying to call "model.destroy()":
Here's my Backbone code:
var Team = Backbone.Model.extend({
idAttribute: "_id",
urlRoot: '/api/teams'
});
var TeamCollection = Backbone.Collection.extend({
model: Team
});
var teamCollection = new TeamCollection([]);
teamCollection.url = '/api/teams';
teamCollection.fetch(
{
success: function () {
console.log('teamCollection length:', teamCollection.length);
}
}
);
var UserHomeMainTableView = Backbone.View.extend({
tagName: "div",
collection: teamCollection,
events: {},
initialize: function () {
this.collection.on("reset", this.render, this);
},
render: function () {
var teams = {
teams:teamCollection.toJSON()
};
var template = Handlebars.compile( $("#user-home-main-table-template").html());
this.$el.html(template(teams));
return this;
},
addTeam: function (teamData) {
console.log('adding team:', team_id);
},
deleteTeam: function (team_id) {
console.log('deleting team:', team_id);
var team = teamCollection.where({_id: team_id}); //team IS defined here but I can't confirm the type even when logging "typeof"
console.log('team to delete', typeof team[0]);
console.log('another team to delete?',typeof team[1]);
team.destroy({ //THIS FUNCTION CALL IS THROWING A TYPEERROR
contentType : 'application/json',
success: function(model, response, options) {
this.collection.reset();
},
error: function(model, response, options) {
this.collection.reset();
}
});
}
});
So I am fetching the data from the node.js server, and the server is returning JSON. The JSON has cid's and all that jazz, so those objects were once Backbone models at some point.
I just don't know why the type of team would not be a Backbone model.
Any ideas?
.where returns an array. You need to use .findWhere instead.
Or call destroy for every model in the resulting array.
.where({...}).forEach(function(model){
model.destroy();
});

BackboneJS Uncaught Error: A "url" property or function must be specified

I am getting this error . I am able to preform read, and remove functions using BackboneJs , but i am having error when i execute the add method any help will be appreciated.
JSfiddel path is http://jsfiddle.net/2wjdcgky/
BackboneJS Uncaught Error: A "url" property or function must be specified
$(function() {
Model
var modelContact = Backbone.Model.extend({
defaults: function() {
return {
Id: 0,
Name: "",
Address: ""
};
},
idAttribute: "Id"
});
ModelCollection
var contactCollection = Backbone.Collection.extend({
model: modelContact,
url: function() {
return 'api/Contact';
},
add: function(model) {
this.sync("create", model); // Error On create
},
remove: function(model) {
this.sync("delete", model); //Runs Fine
}
});
var contacts = new contactCollection;
View
var contactView = Backbone.View.extend({
tagName: "tr",
events: {
"click a.destroy": "clear"
},
template: _.template($("#newContacttemplate").html()),
initialize: function() {
this.model.on("change", this.render, this);
this.model.on('destroy', this.remove, this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
clear: function(e) {
contacts.remove(this.model); // runs fine
}
});
Main View
var main = Backbone.View.extend({
el: $("#contactApp"),
events: {
"click #btnsave": "CreateNewContact"
},
initialize: function() {
this.Nameinput = this.$("#contactname");
this.Addressinput = this.$("#contactaddress");
contacts.on("add", this.AddContact, this);
contacts.on("reset", this.AddContacts, this);
contacts.fetch();
},
AddContact: function (contact) {
console.log("AddContact");
var view = new contactView({ model: contact });
this.$("#tblcontact tbody").append(view.render().el);
},
AddContacts: function () {
console.log("AddContacts");
contacts.each(this.AddContact);
},
CreateNewContact: function (e) {
console.log(e);
//Generate an error "BackboneJS Uncaught Error: A "url" property or function must be specified"
contacts.add({ Name: this.Nameinput.val(), Address: this.Addressinput.val() });
}
});
var m = new main;
});
Your JSFiddle was missing Backbone references and all.
Working update: http://jsfiddle.net/apt7hchL/2/
Much simpler code (no need to define those add and remove methods on the collection!). Also more common Javascript coding style conventions.
Please note I had to manually generate an "Id" attribute to allow creating more than one contact. As you are making Id = 0 by default, second model with same is not added, as Backbone sees a model with id=0 is already in the collection.
When you want to save, call the model.save() method. Don't call sync manually, you'll normally don't need to!
For the model to be saved to the database before being added to the collection, use:
createNewContact: function (e) {
e.preventDefault();
var self = this;
var newContact = new ContactModel({
Name: this.$("#name").val(),
Address: this.$("#address").val()
});
newContact.save({ success: function(model){
self.collection.add(model);
});
//clear form
this.$("#name").val("");
this.$("#address").val("");
}
Sync method tries to sync to a server setup to handle it, with CRUD abilities. If thats not what you're looking for, and you just want to display this information on the client side, instead of using sync, you should use Collection.add(model) and Collection.remove(model)

How do i stop a single model adding his id to url in Backbone?

i have a problem with backbone.js. I'm creating a frontend for an existing api, for me unaccessable. The problem is that when I try to add a new model to a collection, i can see in my firebug that every time backbone tries to create the model it appends the attribute name to the url.
Example:
default url = /api/database
when i perform a GET = /api/database
when i perform a GET/POST with object {"name": "test"} =
/api/database/test is the result
Anyone knows how to avoid that behaviour?
Greetings Kern
My View:
window.databaseView = Backbone.View.extend({
el: '#content',
template: new EJS({url: 'js/templates/databaseView.ejs'}),
initialize: function() {
var self = this;
this.collection.fetch({
success: function() {
console.log(self.collection);
var test = self.collection.get("_system");
console.log(test);
self.collection.get("_system").destroy();
self.collection.create({name: "test"});
}
});
},
render: function(){
$(this.el).html(this.template.render({}));
return this;
}
});
Model:
window.Database = Backbone.Model.extend({
initialize: function () {
'use strict';
},
idAttribute: "name",
defaults: {
}
});
Collection:
window.ArangoDatabase = Backbone.Collection.extend({
model: window.Database,
url: function() {
return '../../_api/database/';
},
parse: function(response) {
return _.map(response.result, function(v) {
return {name:v};
});
},
initialize: function() {
this.fetch();
},
getDatabases: function() {
this.fetch();
return this.models;
},
dropDatabase: function() {
},
createDatabse: function() {
}
});
By default, Backbone create models URLs this way: {collection url}/{model id}.
It consider the collection URL to be a base URL in a RESTful way.
Here you only want to set the Model url property to the URL you whish to call. That'll overwrite the default behavior. http://backbonejs.org/#Model-url

Categories

Resources