I am very new to Backbone and am doing a simple tutorial. I keep running into an error that I dont understand. Here is my code.
(function($) {
dataModel = new Backbone.Model.extend({
data: [
{text: "Google", href: "www.google.com"},
{text: "Yahoo", href: "www.yahoo.com"},
{text: "Youtube", href: "www.youtube.com"},
]
});
var View = Backbone.View.extend({
initialize: function(){
this.template = $('#list-template').children();
},
el: $('#container'),
events: {
"click button": "render"
},
render: function(){
var data = this.model.get('data');
for(var i = 0, l = data.length; i < l; i++){
var li = this.template.clone().find('a').attr('href', data[i].href).text(data[i].text).end();
this.el.find('ul').append(li);
}
}
});
var view = new View({ model: dataModel });
})(jQuery);
When I call this.model.get('data') I get the error TypeError: Object function (){return a.apply(this,arguments)} has no method 'get'. Please show me my error. Thanks.
All the properties and methods you pass when extending a model are set on its prototype not as its attributes, and dataModel here is not a Backbone model instance but a Backbone Model subclass. If done this way to access the data property you'd need to instantiate the model and do a modelInstance.data rather then modelInstance.get('data') as if it would be when data would be set as model attribute as shown in the example below.
What you wanted to do here was
var dataModel = new Backbone.Model({ // without the extend!
data: [
{text: "Google", href: "www.google.com"},
{text: "Yahoo", href: "www.yahoo.com"},
{text: "Youtube", href: "www.youtube.com"},
]
});
as you want to create an instance of a model rather then subclass Backbone.Model class. Extend method is used to subclass core Backbone classes – views, models, collections and routers.
Your 'dataModel' is just the definition of the Model. You need to create a model instance by calling new dataModel() when you pass it to the view.
a model define`s only the structure of one data element
Model
defaults:
text : null
href : null
what you need is a Collection
Collection
model : Model
you can then set data to the collection with .add
Collection.add([
{text: "Google", href: "www.google.com"},
{text: "Yahoo", href: "www.yahoo.com"},
{text: "Youtube", href: "www.youtube.com"},
])
to get the data from the collection look here
http://underscorejs.org/#collections
Hope it helps
Related
Model code:
App.Team = Backbone.RelationalModel.extend({
urlRoot: 'data/json/team',
/*urlRoot: 'data/json/myteam.txt',*/
idAttribute: 'id',
relations:
...
app.currentTeam = new App.Team({id:11});
View:
var trophiesBox = JST['teaminfo/history/leftbox'](app.currentTeam.attributes);
$("#teamhistory_leftbox").append(trophiesBox);
for (var i = 1; i <= app.currentTeam.attributes.history.length; i++) {
var historyData = app.currentTeam.attributes.history.get(i);
var historyRow = JST['teaminfo/history/row'] (historyData.attributes);
$("#teamhistory_table_body").append(historyRow);
}
I'm getting "Uncaught TypeError: Cannot read property 'attributes' of undefined"
on var historyRow = JST['teaminfo/history/row'] (historyData.attributes); line.
Before I had problems defining historyData, probably since it is a model in a collection (app.currentTeam.attributes.history) inside of another model (app.currentTeam). I was getting [Object] (app.currentTeam.attributes.history) doesn't have a 'get' method type of error. Now it passes fine, but I get another error message in the next line, so I wonder what is wrong with my code here.
app.currentTeam.attributes loads fine, so I guess there is a problem retrieving attributes of a model that is inside a collection within another model.
Edit: relation of Team and History collection:
{
type: Backbone.HasMany,
key: 'history',
relatedModel: 'App.HistoryItem',
collectionType: 'App.History',
reverseRelation: {
key: 'team',
includeInJSON: 'id',
}
}
You get Model from Collection from wrong method
app.currentTeam.attributes.history.get(i);
You have to use
app.currentTeam.get('history') // you got collection
app.currentTeam.get('history').at(i); // you got model from collection by index
Update1:
Try use iterator for get elements from collection:
var trophiesBox = JST['teaminfo/history/leftbox'](app.currentTeam.attributes);
$("#teamhistory_leftbox").append(trophiesBox);
app.currentTeam.get('history').each(function(historyData, i) {
var historyRow = JST['teaminfo/history/row'](historyData.attributes);
$("#teamhistory_table_body").append(historyRow);
}, this);
Have you checked Backbone Associations. This might be of some help.
So, I'm just started using backbone.js and what I have some troubles understanding how to define two collections where one of them is within a model of the other one.
The REST API looks something like this:
/sites <--- one collection
/sites/123/entities <--- another collection
/sites/123/entities/abc <--- a specific entity within a specific site
This is how the Sites collection is defined:
var ROOT = 'http://localhost:5000';
Site = Backbone.Model.extend({
defaults: {
id: "default",
description: "My site"
}
});
Sites = Backbone.Collection.extend({
model: Site,
url: ROOT + "/sites"
});
How would the Entities collection and model look like to achieve this?
This is how I imagine it would be, though haven't tested it:
Entity Model/Collection:
var ROOT = 'http://localhost:5000';
Entity = Backbone.Model.extend({
defaults: {
id: "default",
description: "My Entity"
}
});
Entities = Backbone.Collection.extend({
model: Entity,
ownUrl: '/entities',
site: {},
url: function() {
return (this.site? this.site.url() : '') + this.ownUrl;
},
initialize: function(options) {
this.site = options.site;
}
});
Site Model/Collection:
Site = Backbone.Model.extend({
defaults: {
id: "default",
description: "My site"
}
});
Sites = Backbone.Collection.extend({
model: Site,
url: ROOT + "/sites",
});
Example of setting up:
// New collection of site
var site1 = new Site({id: 'site1', description: "This is site 1"});
var site2 = new Site({id: 'site2', description: "This is site 2"});
// Add entities to sites
var entityCollection1 = new Entities([
{id: 'entity1'},
{id: 'entity2'}
], {site: site1});
// Collection of sites
var mySites = new Sites([site1,site2]);
Edited:
According to Backbone documents, from version 1.1, this.options is not attached automatically anymore. Sorry I was still refer to the 0.9.x code.
In 1.1, Backbone Views no longer have the options argument attached as
this.options automatically. Feel free to continue attaching it if you
like. http://backbonejs.org/#changelog
For the url, my bad, it was supposed to be this.site.url() because it's a model's function which will take the default value as [collection.url/modelId].
In this case, it should return 'site/1' for Model site1 (i.e. 'site/2') then you append whatever your entity collection url will be.(ex: '/entities').
This makes your Entities collection url for site 1 become: /site/1/entities. Any model in this collection will have url /site/1/entities/[modelId]
I want to pass an array to a view like this
this.account_nav = new AccountNav.View({
views: [
{ ref: new Member.Views.AccountNav({ model: this.model }), id: 'viewA' },
{ ref: new Member.Views.SettingsNav({ model: this.model}), id: 'viewB' }
]
});
However there is an error:
Uncaught Error: The argument associated with selector '' is defined
and a View. Set manage property to true for Backbone.View
instances. backbone.layoutmanager.js:208
pointing to
this.account_nav = new AccountNav.View({
Any ideas why I get this error?
If your View definition is created using Backbone.Layout.extend rather than Backbone.View.extend, this issue shouldn't arise.
To cite the Layout Manager documentation example:
var LoginView = Backbone.Layout.extend({
template: "#login-template"
});
versus
var LoginView = Backbone.View.extend({
// [...]
});
https://github.com/tbranyen/backbone.layoutmanager/wiki/Example-usage#structuring-a-view
backbone Model,board:
define([
'underscore',
'backbone',
'collections/lists',
'iobind',
'iosync'
], function( _, Backbone, Lists,ioBind,ioSync) {
var BoardModel = Backbone.Model.extend({
urlRoot: 'board',
noIoBind: false,
socket: io.connect(''),
idAttribute: '_id',
defaults: {
title: 'One Thousand and One Nights'
},
initialize: function() {
this.id = 1;
this.lists = new Lists;
this.socket.emit('joinBoard',this.id);
_.bindAll(this, 'getBoard');
this.ioBind('initBoard', this.getBoard, this);
},
getBoard: function(data){
this.set(data.data.board[0]);
}
});
return BoardModel;
});
backbone View: boardView:
var IndexView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing elements in the HTML.
el: '#board',
// Board template html
template: Mustache.render(Template.board),
events: {
},
initialize: function() {
//Init Data
this.model = new Board();
// var lists = {
// lists: [
// {name: "To Do",
// cards:[
// {name: "Art work for A."},
// {name: "B Prototype."},
// {name: "C prototype."}
// ]
// },
// {name: "Doing",
// cards: [
// {name: "Art work for A."}
// ]
// },
// {name: "Done"}
// ]
// }
// var partial = {card: Template.card_in_list};
// var listHtml = Mustache.render(Template.list,lists,partial);
// template = $(this.template).find('.list-area').append(listHtml);
},
render: function() {
console.log(this.model);
console.log(this.model.toJSON());
var partial = {card: Template.card_in_list};
var listHtml = Mustache.render(Template.list,this.model,partial);
template = $(this.template).find('.list-area').append(listHtml);
this.$el.html(template);
}
});
in View function: render function, the console.log get different result.
console.log(this.model) can get correct object result:
child
_callbacks: Object
_changing: false
_escapedAttributes: Object
_ioEvents: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object
__v: 0
_id: "50b750a7795f285d4e000014"
created: "2012-11-29T12:10:15.269Z"
description: "simple is better, but not simpler"
dueDate: "2012-11-29T12:10:15.269Z"
lists: Array[6]
status: true
title: "test board unique"
__proto__: Object
changed: Object
cid: "c1"
getBoard: function () { [native code] }
id: "50b750a7795f285d4e000014"
lists: child
__proto__: ctor
but this.model.toJSON() only get model default values:
Object
title: "One Thousand and One Nights"
__proto__: Object
it confuse me. anyone know why reason the same model get different result.
In a Backbone Model, your business values (description, title ...) are store in the attributes attribute. When you call toJSON() on your model, what it does is it takes the attributes values, and remove the Backbone.Model object framework's functions and attributes.
When you manually want to set model attributes, you want to use set. I don't know what is in you data.data object, so you should check the doc : http://backbonejs.org/#Model-set
set model.set(attributes, [options])
Set a hash of attributes (one or
many) on the model. If any of the attributes change the models state,
a "change" event will be triggered, unless {silent: true} is passed as
an option. Change events for specific attributes are also triggered,
and you can bind to those as well, for example: change:title, and
change:content. You may also pass individual keys and values.
note.set({title: "March 20", content: "In his eyes she eclipses..."});
book.set("title", "A Scandal in Bohemia"); If the model has a validate
method, it will be validated before the attributes are set, no changes
will occur if the validation fails, and set will return false.
Otherwise, set returns a reference to the model. You may also pass an
error callback in the options, which will be invoked instead of
triggering an "error" event, should validation fail. If {silent: true}
is passed as an option, the validation is deferred until the next
change.
I found i trigger boardView.render twice. when i change code:
a = new boardView;
a.render();
to
a = new boardView;
i got the thing done.
by the way thanks Marcel Falliere's comments.
I'm kind of new to backbone coming from knockout.js and I'm trying to get over this simple hump. I have this code:
$(function(){
window.Student = Backbone.Model;
window.Students = Backbone.Collection.extend({
model: Student,
url: 'test.php'
});
window.students = new Students();
window.AppView = Backbone.View.extend({
el: $('#container'),
initialize: function() {
Students.bind('reset', this.render);
},
render: function(){
console.log(Students.toJSON());
}
});
window.appview = new AppView();
$('#test').click(function(){
//var students = new Students();
students.fetch();
var q = students.toJSON();
console.log(q);
/*
students.create({
name: 'John',
grade: 'A'
}); */
})
});
My server sends the following JSON:
[{"id": "1233","name": "Karen","grade": "C"},{"id": "1234","name": "Susan", "grade": "F"}]
When I click the button and look at the console in Chrome, I see:
First Click:
[] - Corrected -just an empty array
Second Click:
[
Object
grade: "C"
id: "1233"
name: "Karen"
__proto__: Object
,
Object
grade: "F"
id: "1234"
name: "Susan"
__proto__: Object
]
First question is why does it take two clicks? Second: How can I simply assign/bind the grade(both as a collection and by id) to a textbox, <li> or other UI element(better yet make it observable when ajax pops in).
The console message you are seeing is from the click event handler. The console message from the render method never gets called.
You don't see anything in the first log message because fetch is an asynchronous method, so when you call toJSON right after fetch, the collection hasn't been populated from the fetch method yet.
There are a couple changes you need to make to your code to get it work as expected.
First, you need to pass in the collection when you instantiate the view
//original
window.appview = new AppView();
//new
window.appview = new AppView({ collection: window.students });
Then within the view you need to bind to the reset event on the collection that was passed in to the constructor. (You have to bind to an instantiated object, and not the definition of an object like you were originally doing)
window.AppView = Backbone.View.extend({
el: $('#container'),
initialize: function() {
_.bindAll(this, 'render');
this.collection.bind('reset', this.render);
},
render: function(){
console.log(this.collection.toJSON());
}
});
Now comment out the console log message in your click event, then you will only have to click once, and you will see a console log message from the render method.
$('#test').click(function(){
//var students = new Students();
students.fetch();
//var q = students.toJSON();
//console.log(q);
/*
students.create({
name: 'John',
grade: 'A'
}); */
})