I'm new to Backbone.js and I've been having a real problem. I want to list firstname and lastname of members and put them in a table. For some reasons, I'm missing an important point and it's not working. My code:
var members = Backbone.Model.extend({
urlRoot: "/get_members",
});
var member_collection = Backbone.Collection.extend({
model : members,
url : "/get_members",
});
var member_list_view = Backbone.View.extend({
el : $('#members'),
initialize : function() {
this.collection.bind("add", this.render, this);
},
render: function() {
var $tbody = this.$("tbody");
_.each(this.model.models, function(data) {
this.$el.append(new member_view({model : data}).render().el);
}, this);
return this;
}
});
var member_view = Backbone.View.extend({
tagName : "tr",
template : _.template($("#member-template").html()),
render : function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var router = Backbone.Router.extend({
routes : {"members" : "member_router"},
member_router : function() {
var member_colls = new member_collection();
var member_viewer = new member_list_view({collection : member_colls});
member_colls.fetch();
}
});
My HTML:
<script type="text/template" id="member-template">
<table id="members">
<thead>
<tr><td>Firstname</td><td>Lastname</td></tr>
</thead>
<tbody>
<tr><td><%= firstname %></td><td><%= lastname %></td></tr>
</tbody>
</table>
</script>
Any help is greatly appreciated. Thanks,
Ok. There are many problems with the above code:
1) Temaplate member-template
You use the member template for the member_view however it contains the entire table instead of just the member row. Your template should be like:
<script type="text/template" id="member-template">
<td><%= firstname %></td><td><%= lastname %></td>
</script>
2) member_list_view has this.collection.bind while render() uses this.model.models
You should have it consistent as:
var member_list_view = Backbone.View.extend({
el : $('#members'),
initialize : function() {
this.model.bind("add", this.render, this);
},
render: function() {
_.each(this.model.models, function(data) {
this.$el.append(new member_view({model : data}).render().el);
}, this);
return this;
}
});
For a full working version of your code, take a look at this JSFiddle
Related
Im trying to make an Underscore template in my Backbone application, but my scoping must be off or something, because Underscore thinks my variable is not defined. Im getting "Uncaught ReferenceError: dictionary is not defined."
Here is the template code:
<script type="text/template" id="new_template">
<table class="table striped">
<tbody>
<% _.each(dictionary, function(user){ %>
<tr>
<td> <%= user.get('word')%></td>
<td> <%= user.get('definition')%></td>
</tr>
<% }) %>
</tbody>
</table>
</script>
And here is the logic in app.js that defines my inline template variable calls:
(function($){
//---------SINGLE ENTRY MODEL----------
var Entry = Backbone.Model.extend({
defaults: function(){
return{
word: '',
definition: ''
}
}
});
//------------ENTRY MODEL COLLECTION------------
EntryList = Backbone.Collection.extend({
model: Entry
});
//-----INSTANCIATE COLLECTION----
var dictionary = new EntryList();
var saved = new EntryList();
//-----SINGLE ENTRY VIEW------
var EntryView = Backbone.View.extend({
model: new Entry(),
tagName:'div',
className: 'singleEntry',
events:{
'click .edit': 'edit',
'click .delete': 'delete',
'keypress .definition': 'updateOnEnter',
'click .save': 'save'
},
initialize: function(){
// this.template = _.template($("#dictionary_template").html());
this.template = _.template($("#new_template").html());
},
delete: function(ev){
ev.preventDefault;
dictionary.remove(this.model);
saved.remove(this.model);
},
edit: function(ev){
ev.preventDefault;
this.$('.definition').attr('contenteditable', true).focus();
},
save: function(ev){
ev.preventDefault;
saved.add(this.model);
dictionary.remove(this.model);
saved.comparator = 'word';
console.log(this.model.toJSON());
},
close: function(){
var definition = this.$('.definition').text();
this.model.set('definition', definition);
this.$('.definition').attr('contenteditable', false).blur();
},
updateOnEnter: function(ev){
if(ev.which == 13){
this.close();
}
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
//--------------DICTIONARY VIEW------------
var DictionaryView = Backbone.View.extend({
model: dictionary,
el: $('#entries'),
initialize: function(){
this.model.on('add', this.render, this);
this.model.on('remove', this.render, this);
},
render: function(){
var self = this;
self.$el.html('');
_.each(this.model.toArray(), function(entry, i){
self.$el.append((new EntryView({model: entry})).render().$el);
});
return this;
}
});
//---------SAVED ENTRY VIEW-----------
var SavedView = Backbone.View.extend({
model: saved,
el: $('#saved'),
initialize: function(){
this.model.on('add', this.savedRender, this);
this.model.on('remove', this.savedRender, this);
},
savedRender: function(){
var self = this;
self.$el.html('');
_.each(this.model.toArray(), function(entry, i){
self.$el.append(new EntryView({model: entry}).render().$el);
});
return this;
}
});
//---------TEST VIEW------------------
var TestView = Backbone.View.extend({
el: $('#saved'),
render: function(){
this.$el.html('new event route');
}
});
//-------BINDING DATA ENTRY TO NEW MODEL VIEW-------
$(document).ready(function(){
$('#new-entry').submit(function(ev){
var entry = new Entry({word: $('#word').val(), definition: $('#definition').val() });
dictionary.add(entry);
dictionary.comparator = 'word';
console.log(dictionary.toJSON());
$('.form-group').children('input').val('');
return false;
});
var appView = new DictionaryView();
var savedView = new SavedView();
});
//--------------ROUTER----------------
var Router = Backbone.Router.extend({
routes:{
'':'home',
'new': 'newEvent'
}
});
var router = new Router();
router.on('route:home', function(){
console.log('router home');
router.on('route:newEvent', function(){
console.log('router new');
var testView = new SavedView();
})
});
Backbone.history.start();
})(jQuery);
Update
The way Underscore templates work (once you compile them) is by replacing any non JavaScript keywords they find in ERB-style delimiters (the bee-stings: <% %>) with the values in the object passed in to the compiled template. In your sample code, Underscore is expecting an object with a property named 'dictionary', for example.
The way your template is set up right now, it's expecting a collection (EntryList) of models with the words and definition attributes. However, if you look at your code,
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
is the only time populate your template, in your Entry view, which does not take a collection, but an Entry model.
What you really want to do is to get rid of the for each loop in your template, and let it simply render the model. I would rewrite it like this,
<script type="text/template" id="new_template">
<tr>
<td> <%= word %></td>
<td> <%= definition %></td>
</tr>
</script>
Now, I hope that el in dictionary view is a <tbody>. If it is, then these models will comfortably park their <tr> where they have to go and you can call it a day.
Looking at the defaults of the Entry model it looks like all you'll be populating that model with is { word: '', definition: '' }. In your template, underscore is looking for a variable called dictionary. Yet, you pass an object with the variables word and definition in to the template.
More importantly, I am not sure your view building logic makes compete sense. You're building child views out of EntryView in your DictionaryView and then you run a for each loop in your template?
How would I create a view and template from either this model or collection? I can console log the data I want. I'm stuck on the view and template part. Thanks.
var Weather = Backbone.Model.extend({
urlRoot: "http://api.openweathermap.org/data/2.5/weather?q=New%20York&mode=json&units=imperial",
initialize: function (response) {
console.log(response.wind.speed);
console.log(response.main.temp);
console.log(response.name);
console.log(response.main.temp_min);
console.log(response.main.temp_max);
//console.log();
return response;
}
});
var WeatherCollection = Backbone.Collection.extend({
model: Weather,
url: 'http://api.openweathermap.org/data/2.5/weather?q=New%20York&mode=json&units=imperial',
parse: function(response) {
console.log(response.wind.speed);
console.log(response.main.temp);
console.log(response.name);
console.log(response.main.temp_min);
console.log(response.main.temp_max);
//console.log();
return response;
}
});
I would probably do something like this to start with:
var WeatherItemView = Backbone.View.extend({
template: _.template($('#weather-template').html()),
render: function () {
var content = this.template({
weather: this.model
});
this.$el.html(content);
return this;
}
});
var WeatherListView = Backbone.View.extend({
initialize: function () {
this.listenTo(this.collection, 'sync', this.render);
},
render: function () {
this.collection.each(function (weather) {
var subView = new WeatherItemView({
model: weather
});
this.$el.append(subView.render().$el);
}, this);
return this;
}
});
$(document).ready(function () {
var weathers = new WeatherCollection();
weathers.fetch(); // assuming its accessing an api endpoint.
var weathersView = new WeatherListView({
collection: weathers
});
$('body').html(weathers.render().$el);
});
<!-- template for one weather item view -->
<script id='weather-template' type="text/template">
<%= weather.escape('name') %>
</script>
I have a project like googleReader.
I have to page on server:
/feed/:
returns list of feeds
/item/:
return list of feed items. if feedId not set so returns all items.
Now in first page I get all items and show in page. But I want if user clicked on link(feed item) item list gets update.
SCRIPT
var Feeds = Backbone.Collection.extend({
urlRoot: "/feed/?format=json", // or "/feed/?format=json&feed_id=oiasudkj34897"
});
var Items = Backbone.Collection.extend({
urlRoot: "/item/?format=json",
});
var items = new Items();
var FeedList = Backbone.View.extend({
el: '#feed-ul',
render: function(){
var feedListObj = this;
var feeds = new Feeds();
feeds.fetch({
success: function(feeds){
var feedTemplate = _.template($('#feed-list-template').html(), {feeds: feeds.models});
feedListObj.$el.html(feedTemplate);
$('#img-loading-feed').css('display', 'none');
}
});
},
events: {
'click .feed-li' : 'showFeedItem'
},
showFeedItem: function(e){
// ???
}
});
var NewFeedItems = Backbone.View.extend({
el: '#item-ul',
render: function(){
var newItemListObj = this;
items.fetch({
success: function(){
var itemTemplate = _.template($('#item-list-template').html(), {items: items.models});
newItemListObj.$el.html(itemTemplate);
$('#img-loading-item').css('display', 'none');
}
});
},
events: {
'click .feed-item' : 'itemSlideDown'
},
itemSlideDown: function(e){
var itemId = $(e.currentTarget).attr('id');
$('#itemview-'+itemId).toggle(function() {}, function() {});
}
});
var LastItem = Backbone.View.extend({
el: '#item-ul',
render: function(){
var itemListObj = this;
items.fetch({
success: function(){
var itemTemplate = _.template($('#item-list-template').html(), {items: items.models});
itemListObj.$el.html(itemTemplate);
$('#img-loading-item').css('display', 'none');
}
});
},
events: {
'click .feed-item' : 'itemSlideDown'
},
itemSlideDown: function(e){
// alert($(e.currentTarget).attr('id'));
var itemId = $(e.currentTarget).attr('id');
$('#itemview-'+itemId).toggle(function() {}, function() {});
}
});
var Router = Backbone.Router.extend({
routes: {
'': 'home'
}
});
var feedList = new FeedList();
var lastItem = new LastItem();
var router = new Router();
router.on('route:home', function(){
feedList.render();
});
router.on('route:home', function(){
lastItem.render();
});
Backbone.history.start();
TEMPLATE
<script type="text/template" id="feed-list-template">
<% _.each(feeds, function(feed)
{ %>
<li class='feed-li' id="feed-<%= feed.get('id') %>">
<a id="<%= feed.get('id') %>" href="#/<%= feed.get('id') %>">
<%= feed.get('title') %>
</a>
</li>
<%
})
%>
</script>
<script type="text/template" id="item-list-template">
<%
_.each(items, function(item)
{
%>
<li class='prettyprint tar dr fs14' id="item-<%= item.get('id') %>">
<%=
//other template codes
%>
</li>
<%
})
%>
</script>
how I can do this?
In my view.js I am calling the wine-list-tpl (template), as below you can see this. But nothing is showing to the index page. Please help.
IN index.html
<script type="text/template" id="wine-list-tpl">
Hello world
<%= name %>
</script>
.......
<div class="wine-list"></div>
IN view.js
var WineLists = Backbone.View.extend({
el:'.wine-list',
template : _.template($('#wine-list-tpl').html()),
render: function(){
console.log("rendering is ok");
this.$el.html( this.template( this.model.toJSON() ));
return this;
}
});
var WineList = Backbone.View.extend({
model:wines,
initialize: function(){
var self=this;
wines.fetch();
_.each(this.model.toArray(), function(wine, i){
$('#wine-list').append((new WineLists({ model : wine })).render().$el);
});
}
});
var wineListView = new WineList();
I have added some data in to my local storage and i just want to show those data to that particular div element.
In WineLists view i am getting all those data. means when i am writing, console.log(this.model.get('name')); I got my desired data, but i only want to show those data through template to that particular div. what should i do, please suggest.
var WineLists = Backbone.View.extend({
model: new Wine(),
template : _.template($('#myTpl').html()),
render: function(){
//console.log(this.model.get('name'));
this.$el.html(this.template(this.model.toJSON()));
return this;
} });
var WineList = Backbone.View.extend({
model:wines,
el:'.wineDiv',
initialize: function(){
var self=this;
wines.fetch();
_.each(this.model.toArray(), function(wine, i){
self.$el.append((new WineLists({ model : wine })).render().$el);
});}});
var wineListView = new WineList();
//please check ur DOM manipulation i think there is some error in ur HTML try to change call //sequence
This is how your code should have been for expected results.
var WineLists = Backbone.View.extend({
el: '.wine-list',
render: function () {
var _self = this;
this.collection.each(function (model) {
var view = new WineListItemView({
model: model
})
view.render();
view.$el.appendTo(_self.el);
})
return this;
}
});
var WineListItemView = Backbone.View.extend({
template: _.template($('#wine-list-tpl').html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var wines = new Backbone.Collection([{
name: 'wine1'
}, {
name: 'wine2'
}])
var wineListView = new WineLists({
collection: wines
});
wineListView.render()
I'm displaying a table of categories with Backbone. I created two views:
RowView (containing a single tr)
TableView (containing table structure)
The definitions:
RowView = Backbone.View.extend({
el: "#content table tbody",
initialize: function() {
this.render();
},
render: function(){
var params = { name: this.model.get('name'), route: this.options.route };
var template = _.template( $("#rowTemplate").html(), params);
this.$el.append(template);
},
events: {
"click #name": "clickHandler"
},
clickHandler: function( event ) {
console.log('Browse subcategories of ' + this.model.get('name'));
}
});
TableView = Backbone.View.extend({
el: "#content",
initialize: function(){
this.render();
},
render: function(){
var row = new this.collection();
var that = this;
row.fetch({
success: function() {
console.log('Collection fetch succeeded');
var params = { title: that.options.title,
counter: row.at(0).get('counter'),
route: that.options.route
};
var template = _.template( $("#tableTemplate").html(), params);
that.$el.html( template );
// RowView's are created by iteration here
for(var x = 1; x < row.length; x++) {
var params = { model: row.at(x), route: that.options.route };
var view = new RowView(params);
}
}
});
}
});
As you can see, I've attached a click event at the RowView.
RowView template:
<script type="text/template" id="rowTemplate">
<tr>
<td id="name" class="fill"><%= name %></td>
<td>Editar</td>
</tr>
</script>
Clicking any #name triggers the handler in all instance of the view. So when clicking one category I get:
Browse subcategories of category1 127.0.0.1:68
Browse subcategories of category2 127.0.0.1:68
etc...
As far as I know, that's because all RowView's are delegated to the same el.
The first thing I though about was adding the category name to the rowTemplate and compare the value in the DOM with the value in the view to see which one actually triggers the event.
But that solutions look really ugly. What's the correct way of accomplishing this in Backbone?
EXTRA: Is it considered better if I only create one view, and iterate in the template to generate the rows?
EDIT: I think the provided code is enough. Otherwise I can add them.
you can modify RowView like this :
RowView = Backbone.View.extend({
container: '#content table tbody',
tagName: 'tr',
initialize: function() {
this.render();
},
render: function() {
var params = {
name: this.model.get('name'),
route: this.options.route
};
var template = _.template($("#rowTemplate").html(), params);
this.$el.html(template).appendTo(this.container);
},
events: {
"click .fill": "clickHandler"
},
clickHandler: function(event) {
console.log('Browse subcategories of ' + this.model.get('name'));
}
});
and RowView template:
<script type="text/template" id="rowTemplate">
<td class="fill"><%= name %></td>
<td>Editar</td>
</script>
Backbone.js will create a tr element. then this.$el.html(template).appendTo(this.container) fill the tr element with template and append to #content table tbody.
just like that, RowView's events be delegated on RowView's el, not #content table tbody.
You have more than one element with the same id on your page, due to all of your rows having the
<td id="name" class="fill"> element.
Element IDs should be unique within your document.
One solution would be to distinguish the rows in your template, and use events as a function to set the proper ID.
Template:
<script type="text/template" id="rowTemplate">
<tr>
<td id="name-<%= name %>" class="fill"><%= name %></td>
<td>Editar</td>
</tr>
Events function:
events: function(){
_events = {};
_events["click #name-" + this.model.get('name')] = "clickHandler";
return _events;
}
Try this
RowView = Backbone.View.extend({
container: '#content table tbody',
tagName: 'tr',
// initialize: function() {
// this.render();
// },
render: function() {
var params = {
name: this.model.get('name'),
route: this.options.route
};
var template = _.template($("#rowTemplate").html(), params);
this.$el.append(this.template);
},
events: {
"click .name": "clickHandler"
},
clickHandler: function(event) {
console.log('Browse subcategories of ' + this.model.get('name'));
}
});
RowView template (no need for identifying each row view):
<script type="text/template" id="rowTemplate">
<td class="name"><%= name %></td>
<td>Editar</td>
</script>
Then the table view:
...
that.$el.html( template );
// RowView's are created by iteration here
for(var x = 1; x < row.length; x++) {
var params = { model: row.at(x), route: that.options.route };
var view = new RowView(params);
that.$el.find('tbody').append(view.el);
view.render()
}
...