Backbone JSON collection to template output - javascript

I asked a question on this yesterday which helped alot.
I have rewrote most of the code by following tutorials and youtube videos as well as help on stackoverflow however i am unsure what i am doing wrong when pushing the JSON data to the underscore template.
Basically i want to take the data from the json array, loop through it and display it. I've seen tutorials that do this through .get but weren't using an json array. Any help is appreciated.
My code looks like this: (I've put a comment on the line i guess things are going wrong)
<body>
<div class="News"></div>
<script type="text/template" id="NewsTemplate">
<table>
<% _.each(NewsCollection, function(item) { %>
<tr>
<td><%= item.title %></td>
</tr>
<% }); %>
</table>
</script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone.js"></script>
<script type="text/javascript">
var NewsModel = Backbone.Model.extend({ });
// backbone collection, gather google news json array
var NewsCollection = Backbone.Collection.extend({
url: 'data.js'
})
var NewsList = Backbone.View.extend({
el: '.News',
template: _.template($("#NewsTemplate").html()),
render: function () {
var that = this;
var NewsItems = new NewsCollection();
NewsItems.fetch({
//guessing im doing something wrong here?
success: function (NewsItems) {
$(this.el).html(that.template({'collection.toJSON': NewsItems.toJSON()}));
}
})
}
});
// Backbone router do action on homepage load
var Router = Backbone.Router.extend({
routes: {
'': 'home'
}
});
var newslist = new NewsList();
var router = new Router();
router.on('route:home' , function (){
newslist.render();
});
Backbone.history.start();
</script>
</body>

Try this :
Replace following lines
success: function (NewsItems) {
$(this.el).html(that.template({'collection.toJSON': NewsItems.toJSON()}));
}
with
success: function (NewsItems) {
$(this.el).html(that.template({ newsItems: NewsItems.toJSON()}));
}
Update template as:
<script type="text/template" id="NewsTemplate">
<table>
<% _.each(newsItems, function(item) { %>
<tr>
<td><%= item.title %></td>
</tr>
<% }); %>
</table>
</script>

Related

Combining multiple templates before render using requireJs & BackboneJs

I am trying to implement the idea of multiple templates per-view in BackboneJs using requireJs and the requiteJs text plugin.
Here is my view code - you can see i have passed in two templates in my define() of witch gets passed through successfully.
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
var $template = $(Template);
$template.prepend(ElementList);
this.template = _.template($template.html());
},
render: function (Template) {
this.$el.html(this.template({posts : this.collection}));
return this;
},
add: function() {
}
});
return BlogPostIndexView; });
You can see i am trying to combine the second template into the html of the first template. This works but unfortunately when i render then i get this....
<div class="outer-wrapper">
<div id="blog-post-wrapper">
<h1>texting views</h1>
</div>
<ul>
<% _.each(posts, function(post){ %>
<li><%= post.title %></li>
<% }); %>
</ul>
I'm missing closing tag for the outer-wrapper, but lets suppose it should be after closing 'ul' tag and your templates looks as follows:
container.html
<div class="outer-wrapper">
<div id="blog-post-wrapper">
<h1>texting views</h1>
</div>
</div>
list.html
<ul>
<% _.each(posts, function(post){ %>
<li><%= post.title %></li>
<% }); %>
</ul>
code:
define([..."container.html", "list.html"...], function (...container, list...) {
...
initialize: function () {
// container:
// no need to compile 'container' if there are no variables..
// list:
this.listTemplate = _.template(list);
}
...
render: function () {
var $container = $(container);
$container.append(this.listTemplate({...}));
this.$el.html($container);
}
BTW: check this !text alternative https://github.com/tbranyen/lodash-template-loader

Backbone.js Uncaught TypeError

I have written the flowing backbone.js program :
<script>
var PostModel = Backbone.Model.extend();
var PostView = Backbone.View.extend({
template: _.template($('#posttemplate').html()),
intialize: function() {
console.log("intializing view");
},
render: function() {
console.log("rendering..");
var htmloutput = this.template(this.model.toJSON());
$('#postcontainer').html(htmloutput);
return this;
}
});
$(document).ready(function() {
var postmodel = new PostModel({title: "hello"});
var postview = new PostView({model: postmodel});
postview.render();
});
</script type="text/javascript">
<script type="text/template" id="posttemplate">
<div> Title: <%= title %> , post: <%= post %> </div>
</script>
<div class="container" id="postcontainer"></div>
when i run the code i get the following error:
Uncaught TypeError: Cannot read property 'replace' of undefined
but it works perfectly fine when i put
template = _.template($('#posttemplate').html()); into the render function.
Your problem is that you're trying to access the template before it exists. The HTML document is parsed from the top to the bottom and when you have
template: _.template($('#posttemplate').html())
then the $('#posttemplate') selector does not return any results because the element containing the template hasn't been parsed yet.
Try moving the
<script type="text/template" id="posttemplate">
<div> Title: <%= title %> , post: <%= post %> </div>
</script>
element up above your first script element.
The reason it works when you put it in the render function is that render is called after the document fires a ready event, at which point the template exists.

Backbone js Model Save more than once

Iam learning Backbone js and so I have started creating sample application.
BTW, iam facing one problem now ie., model is saving more than once in my database. I mean when you click 'Create User' , you'll see a form, so when I click that 'Create User' button, details are getting saved more than once in my DB and so all duplicate users info displayed in the home page.
Actually iam trying to practice this video: https://www.youtube.com/watch?v=FZSjvWtUxYk
The output would look like this: http://backbonetutorials.com/videos/beginner/#/new
Here is my Code:
<html>
<head>
<link rel="stylesheet"
href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap.min.css">
<script type="text/javascript">
/*$.getJSON('api/users/1',function(data){
console.log(data);
});*/
</script>
</head>
<body>
<div class="container">
<h1> User Manager</h1>
<hr/>
<div class="page"></div>
</div>
<script type="text/template" id="user-list-template">
New User
<hr/>
<table class="table stripped">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<% _.each(users, function(user){ %>
<tr>
<td><%= user.get('firstName') %></td>
<td><%= user.get('lastName') %></td>
<td><%= user.get('age') %></td>
</tr>
<% }); %>
</tbody>
</table>
</script>
<script type="text/template" id="add-user-template">
<legend>Create User</legend>
<form class="add-user-form">
First Name <input type="text" id="firstName"/><br/>
Last Name <input type="text" id="lastName"/><br/>
Age <input type="text" id="age"/><hr/>
<input type="submit" value="Create User">
</form>
</script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"></script>
<script type="text/javascript">
var UsersList = Backbone.Collection.extend({
url: 'api/users'
});
var User = Backbone.Model.extend({
urlRoot: 'api/users'
});
var UsersListView = Backbone.View.extend({
el: '.page',
render: function(){
var that = this;
var users = new UsersList();
users.fetch({
success: function(usersList){
var template = _.template($('#user-list-template').html())({users: usersList.models});
that.$el.html(template);
}
});
}
});
var AddUserView = Backbone.View.extend({
el: '.page',
render: function(){
var template = _.template($('#add-user-template').html())({user:null});
this.$el.html(template);
},
events: {
'submit .add-user-form': 'saveOrUpdateUser'
},
saveOrUpdateUser: function(e){
e.preventDefault();
var userDetails = {firstName: $('#firstName').val(), lastName: $('#lastName').val(), age: $('#age').val()};
var user = new User();
user.save(userDetails,{ //SEEMS LIKE HERE HAVING SOME PROBLEM
success: function(){
console.log('INSIDE SUCCESS..');
router.navigate('',{trigger: 'true'});
}
});
}
});
var Router = Backbone.Router.extend({
routes:{
'':'home',
'new':'addUser'
}
});
var router = new Router();
router.on('route:home',function(){
var usersListView = new UsersListView();
usersListView.render();
});
router.on('route:addUser',function(){
var addUserView = new AddUserView();
addUserView.render();
});
Backbone.history.start();
</script>
</body>
</html>
Please suggest me what went wrong and how to fix this?
#MarciaYudkin you'll notice that when you first load your site and create a user, that first user does not save a duplicate. However, the next time you fill out a CreateUser form, the user will get saved twice. What's happening here is that you are suffering from Zombie Views! (Ahh!)
A Zombie View is a view that you thought went away, but in fact remains in the background. Since they're still alive, they are also still bound its view events. The zombie view you detected in your code is:
router.on('route:addUser',function(){
var addUserView = new AddUserView(); // <---Right here!
addUserView.render();
});
Every time a user follows the route:addUser route they end up creating a new AddUserView. This view renders itself and attaches itself to the DOM. You would think that since you "removed" your old view from the DOM, it would simply disappear, right? Well, it does---from the DOM---but not from memory! Since that view still has event bound to the DOM, it does not get garbage collection. When a DOM element that was bound to any previous views is triggered (like by clicking it), the current, as well as all the old, undisposed, views are still bound to it, and all of them respond to the trigger. That's what's happening to you. Here, see this fiddle I cooked up.
How to fix it
The way out of this is to keep a reference to the view around, so that you could properly dispose of it. So, for example, you could attach a reference to the view in your router, and do,
router.on('route:addUser',function(){
// If the view exists, remove it
if (router.addUserView) {
router.addUserView.remove();
router.addUserView.off();
}
router.addUserView = new AddUserView();
router.addUserView.render();
});
Here we call Backbone.View.remove(), which really does this.$el.remove() behind the scences, effectively removing the DOM elements referred to by our view and releasing the bound events. Now our view can be garbage collected!
You can see how I answered this question recently for another angle on this: Saving User Data more than once. And I think I'd be remiss if I didn't include Derick Bailey's seminal article on zombie views (from where I got most of my information), Zombies! RUN! (Managing Page Transitions In Backbone Apps)

Backbone getting model for a clicked element

Say I have a collection of sportsmen. I juste display their names and when there's a click on a name, I'd like to dislay some more details. But that implies I have to retrieve the associate model.
So I have a model, a collection and then two views, one that takes care of rendering each individual name, and the other one manages the list of those names. And then I'll have to create a view to display the details of the clicked sportsman.
But I can't figure out how to retrive the associate model. If I place my event in the view that manages the list, it triggers on click but there's an error saying that the model is undefined.
And if the event listener is located in the view that renders each individual name, it doesn't do anything on click.
Here's my code :
Index.html
<script id="nameListTemplate" type="text/template">
<%= first %> <%= last %>
</script>
<script id="contactTemplate" type="text/template">
<ul>
<li><%= first %></li>
<li><%= last %></li>
<li><%= age %></li>
<li><%= sport %></li>
<li><%= category %></li>
</ul>
</script>
<script src="js/lib/jquery.min.js"></script>
<script src="js/lib/underscore-min.js"></script>
<script src="js/lib/backbone-min.js"></script>
<script src="js/models/sportsManModel.js"></script>
<script src="js/collections/sportsMenCollection.js"></script>
<script src="js/views/nameView.js"></script>
<script src="js/views/nameListView.js"></script>
<script src="js/app.js"></script>
<ul id="sportsMenName"></ul>
<div id="sportsManDetails"></div>
sportsManModel.js
var app = app || {};
app.SportsManModel = Backbone.Model.extend({});
sportsMenCollection.js
var app = app || {};
app.SportsMenCollection = Backbone.Collection.extend({
model: app.SportsManModel
});
nameView.js
var app = app || {};
app.NameView = Backbone.View.extend({
tagName: 'li',
className: 'sportsMan',
template: _.template($('#nameListTemplate').html()),
/*events: {
'click .sportsMan': 'showSportsManDetails'
},
// here it doesn't work at all
showSportsManDetails: function(e){
alert(this.model.get('first'));
},*/
render: function(){
this.$el.append(this.template(this.model.attributes));
return this;
}
});
nameListView.js
var app = app || {};
app.NameListView = Backbone.View.extend({
el: '#sportsMenName',
initialize: function(sportsMen){
this.collection = new app.SportsMenCollection(sportsMen);
this.render();
},
/*events: {
'click .sportsMan': 'showSportsManDetails'
},
// here it says that this.model is undefined
showSportsManDetails: function(e){
alert(this.model.get('first'));
},*/
render: function(){
this.collection.each(function(sportsMen){
this.renderContact(sportsMen);
}, this)
},
renderContact: function(sportsMen){
var nameView = new app.NameView({
model: sportsMen
});
this.$el.append(nameView.render().el);
}
});
The first problem is that you have a view for li.sportsMen element so you don't need to use class .sportsMen in events at all.
And second problem you had wrong method name for click event showSportsManDetails but should be showContactDetail as you have showContactDetail method or rename method name to showSportsManDetails instead.
app.NameView = Backbone.View.extend({
...
events: {
'click': 'showSportsManDetails'
},
showSportsManDetails: function(e){
alert(this.model.get('first'));
},
Here is a working example http://jsbin.com/yutaduvokehu/1/edit
i think the problem may be with the scope , try adding this initalize function in nameView :
initailize : function(){
_.bindAll(this,'showContactDetail');
}

How to fetch data from Backbone collection to Template

i am just writing a simple backbone program. But i am not getting how to fetch data from backbone collection to backbone template. Complete code is written below:
<!doctype html>
<html>
<head>
<title>Backbone tutorial</title>
</head>
<body>
<div class="user">user</div>
<div class="page"></div>
<script type="text/template" id="user-list-template">
I am not able to get data on daya.key
<h1> <%= data.key %> </h1>
</script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="backbone.js"></script>
<script>
This is my Collection code
var Album = Backbone.Collection.extend({
url : "/data.json"
});
This is my view code
var UserList= Backbone.View.extend({
el:'.page',
template:_.template($('#user-list-template').html()),
render : function(){
var that=this;
var album= new Album();
album.fetch({
success:function(album){
var _data = {data : album.models} ;
$(that.el).html(that.template(_data));
}
})
}
});
This is my Router code
var Router = Backbone.Router.extend({
routes: {
"": "home" // #help
}
});
var userList= new UserList();
var router = new Router();
router.on('route:home', function(){
userList.render();
});
Backbone.history.start();
</script>
</body>
</html>
and here is the data.json file
{ "key" : "value to print on template "}
Two modifications i would suggest
1.First check the data feild in your template. Since you are fetching data from the collection it will be array of models.
<h1> <%= data[0].key %> </h1>
or you can use a for loop iterating over the collections
2.The data.json file should look like this
[{"key" : "value"}]
Server needs to return a JSON array of model object for collection.fetch() request. So the data.json should look like this:
[{"key":"value to print on template"},{"key":"another value"}]
And try this collection view render implementation:
Model:
var User = new Backbone.Model.extend({});
Collection:
var Album = new Backbone.Collection.extend({
model: User,
url: "/data.json"
});
//create collection instance
var album = new Album();
View:
var UserList= Backbone.View.extend({
el:'.page',
template:_.template($('#user-list-template').html()),
initialize: function(){
//register a collection data add event handler
this.listenTo(album,"add",this.addUser);
//register a collection data remove event handler
this.listenTo(album,"remove",this.removeUser);
album.fetch();
},
render : function(){
},
addUser: function(user){ //apply model data to view template and append to view element
this.$el.append(this.template(user.toJSON()));
},
removeUser: function(user){
//view state update implementation when data have been removed from collection
}
});
Template:
<script type="text/template" id="user-list-template">
<h1><%= key %></h1>
</script>
div.user view will add/remove user-list view dynamically according to collection data manipulation.
Hope this helpful.

Categories

Resources