I am getting started with Backbone.js but can't seem to get the simplest proof-of-concept working. I have the following code:
$(function() {
var Contact = Backbone.Model.extend({
url: 'contacts.txt'
});
var ContactList = Backbone.Collection.extend({
model: Contact,
url: 'contacts.txt'
});
var Contacts = new ContactList();
var ContactView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#contact-template').html()),
initialize: function() {
_.bindAll(this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var AppView = Backbone.View.extend({
el: '#contacts',
initialize: function() {
Contacts.bind('add', this.addOne, this);
Contacts.fetch();
},
addOne: function(Contact) {
var view = new ContactView({model: Contact});
this.$el.append(view.render().el);
}
});
var app = new AppView();
});
The file contacts.txt contains a simple JSON structure, which is loading fine according to Chrome:
[{"from_acct_id": 5, "ct": 0, "from_display_nm": "Name 1", "to_acct_id": 1},
{"from_acct_id": 3, "ct": 1, "from_display_nm": "Name 2", "to_acct_id": 1},
{"from_acct_id": 2, "ct": 0, "from_display_nm": "Name 3", "to_acct_id": 1},
{"from_acct_id": 4, "ct": 1, "from_display_nm": "Name 4", "to_acct_id": 1}]
For whatever reason, the addOne() function bound to the add event in AppView is never invoked. What could be going wrong?
A Backbone collection's fetch call will fire a reset event. Bind to reset instead.
http://documentcloud.github.com/backbone/#Collection-fetch
Related
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();
}
});
}());
I have this collection:
var items = new bb.Collections.QuotesCollection([
{id: 1, name: "item 1", units: []},
{id: 2, name: "item 2", units: []},
{id: 3, name: "item 3", units: []}
]);
And then I output the array "units" like so:
if(this.model.get('units').length){
$(this.el).append('<strong>Units</strong>');
$(this.el).append('<ul>');
for(x in this.model.get('units')){
$(this.el).append('<li class="unit">' + this.model.get('units')[x] + '</li>');
}
$(this.el).append('</ul>');
}
The code above is only POC stuff, so no formal templating as yet.
events: {
"keypress #addUnit" : "addUnit",
"dblclick .unit" : "deleteUnit"
},
deleteUnit: function(){
this.render(); // what do I put here!?
}
What approach do I take to delete an item (the clicked one) from the "units" array?
this is the quick and dirty method:
Assuming the Array's order is not changed through any other medium, you could do
deleteUnit: function() {
// get the index of the li you are clicking
var index = $('.unit').index(this);
this.model.get('units').splice(index, 1);
this.render();
}
This way you have to remember to empty your view element before every render
render: function() {
this.$el.empty();
...
// business as usual
}
First, you probably want to have a view object for each model, so you'd have a collection view which owns the <ul> and looks like this:
var ParentView = Backbone.View.extend({
render: function() {
var html = '<ul></ul>'; // make your html here (usually with templates)
this.$el.append(html);
this.collection.each(_.bind(this.initChild, this));
return this; // so we can chain calls if we want to.
}
initChild: function(model) {
var child = new ChildView({ model: model });
// this.$() is scoped to the view's el property
this.$('ul').append(child.render().el);
}
});
You'd then set up the child views something like this:
var ChildView = Backbone.View.extend({
events: { 'click .delete', 'deleteModel' },
render: function() {
var html = '';// make your html here (usually with templates)
this.$el.append(html);
return this;
},
deleteModel: function(ev){
ev.preventDefault();
// Removes form the collection and sends an xhr DELETE
this.model.destroy();
this.$el.remove();
}
});
The call to Model#destroy will take care of removing it from the collection and sending a DELETE to the server (assuming you have a URL set up in your collection/model).
As far as i understand you need to delete item from model
Person = Backbone.Model.extend({
initialize: function() {
alert("Welcome to this world");
}
});
var person = new Person({ name: "Thomas", age: 67});
delete person.name
I'm relatively new to Backbone and Underscore and have one of those questions that's not really an issue - just bugging me out of curiosity.
I built a very simple app that allows you to add and remove models within a collection and renders them in the browser. It also has the ability to console.log the collection (so I can see my collection).
Here's the weird thing: the ID's being generated are 1,3,5... and so on. Is there a reason specific to my code, or something to do with BB/US?
Here's a working Fiddle: http://jsfiddle.net/ptagp/
And the code:
App = (function(){
var AppModel = Backbone.Model.extend({
defaults: {
id: null,
item: null
}
});
var AppCollection = Backbone.Collection.extend({
model: AppModel
});
var AppView = Backbone.View.extend({
el: $('#app'),
newfield: $('#new-item'),
initialize: function(){
this.el = $(this.el);
},
events: {
'click #add-new': 'addItem',
'click .remove-item': 'removeItem',
'click #print-collection': 'printCollection'
},
template: $('#item-template').html(),
render: function(model){
var templ = _.template(this.template);
this.el.append(templ({
id: model.get('id'),
item: model.get('item')
}));
},
addItem: function(){
var NewModel = new AppModel({
id: _.uniqueId(),
item: this.newfield.val()
});
this.collection.add(NewModel);
this.render(NewModel);
},
removeItem: function(e){
var id = this.$(e.currentTarget).parent('div').data('id');
var model = this.collection.get(id);
this.collection.remove(model);
$(e.target).parent('div').remove();
},
printCollection: function(){
this.collection.each(function(model){
console.log(model.get('id')+': '+model.get('item'));
});
}
});
return {
start: function(){
new AppView({
collection: new AppCollection()
});
}
};
});
$(function(){ new App().start(); });
if you look in the backbone.js source code you'll notice that _.uniqueId is used to set a model's cid:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L194
that means that every time you create a model instance, _.uniqueId() is invoked.
that's what causing it to increment twice.
I am very new to Backbone and just trying to figure out the way things work. How do I iterate through the "fetched" results?
(function($) {
console.log('Formcontainer init.');
var Field = Backbone.Model.extend({
defaults: {
id: "0",
memberlist_id: "3",
field_name: "sample-field-name",
},
initialize: function() {
console.log('Field model init.');
}
});
var FieldCollection = Backbone.Collection.extend({
defaults: {
model: Field
},
model: Field,
url: 'http://localhost:8888/getstuff/3.json',
initialize: function() {
console.log('FieldCollection init.');
}
});
var ListView = Backbone.View.extend({
el: '#app-container',
initialize: function() {
_.bindAll(this,"render");
console.log('ListView init.')
this.counter = 0;
this.collection = new FieldCollection();
this.collection.fetch();
//this.collection.bind('reset', function() { console.log('xxx')});
//this.collection.fetch();
this.render();
},
events: {
'click #add': 'addItem'
},
render: function() {
var self = this;
console.log('Render called.');
},
addItem: function() {
this.counter++;
this.$("ul").append("<li>hello world" + this.counter + "</li>");
}
});
var listView = new ListView();
})(jQuery);
I get this in my Firebug console:
Formcontainer init.
ListView init.
FieldCollection init.
GET http://localhost:8888/getstuff/3.json 200 OK
Render called.
Field model init.
Field model init.
Field model init.
Field model init.
Field model init.
It seems that the fetch() was called - since "Field model init." is in the console 5 times. But how to I output that? I would want to append the items in an unordered list.
Thanks!
Bind the reset event to your render function...
this.collection.bind('reset', this.render, this);
This gets triggered after the fetch is complete, so you can create the list...
render: function() {
var self = this;
console.log('Render called.');
this.collection.each(function(i,item) {
this.$el.append("<ul>" + item.get("field_name") + "</ul>");
});
},
I'm trying to use the render function in questionListView, and it appears to be running, but is not updating the page.
The template:
<script id="myTemplate" type="text/template">
<p>Test</p>
</script>
Part of the JS:
$(function(){
//Test data
var initialListData = [
{ listName: "Sample Questions", listID: 1},
{ listName: "Default questions", listID: 2}
];
// MODELS ----------------------------------------------------
var questionList = Backbone.Model.extend({
defaults: {
listName: "Name of the list",
listID: 0
}
});
// COLLECTIONS ----------------------------------------------------
var populateList = Backbone.Collection.extend({
model: questionList
});
// VIEWS ----------------------------------------------------
var questionListView = Backbone.View.extend({
template: $("#myTemplate").html(),
render: function () {
console.log('I can see this, but nothing happens...');
var tmpl = _.template(this.template);
$(this.el).append(tmpl(this.model.toJSON()));
return this;
}
});
var AppView = Backbone.View.extend({
el : $("#content"),
initialize: function (){
this.collection=new populateList(initialListData);
this.render();
},
render: function (){
_.each(this.collection.models, function (item) {
this.renderSelect(item);
}, this);
},
renderSelect: function(item){
var populateQuestionList = new questionListView({
model: item
});
this.$el.append(populateQuestionList.render().el);
}
});
var app = new AppView();
} (jQuery));
Thanks!
Are you triggering this in a callback to the document.ready event? If not, your code could be executing before the DOM is actually loaded and ready. Try:
$(function () {
var app = new AppView();
});
A few misc points.
You can do template: _.template($("#myTemplate").html()) to cache the template function as a micro-optimization
You can use this.$el instead of $(this.el) in recent version of backbone. You are already doing this in one place but not both.