I have been trying to pass a model object to be evaluated in my template but had no luck. I tried the following but had no luck
dashboardmodel.js
var myMod = Backbone.Model.extend({
defaults: {
name: "mo",
age: "10"
}
});
myview.js
var dashView = Backbone.View.extend({
el: '.content-area',
this.mymodel = new myMod({}),
template: _.template(dashBoardTemplate, this.mymodel),
initialize: function() {
},
render: function() {
this.$el.html(this.template);
return this;
}
// more javascript code.............
dahboard.html
<p> Hello <%= name %> </p>
PS: I am using the underscore template engine
In addition , your way to pass a model to a view is not flexible, because you would pass an instance of your model, instead of a default model. Thus, you might want to single out
this.mymodel = new myMod({}),
(btw, above line gives me error message in my chrome browser because of "=" sign)
Then, suppose you have an instance A:
A = new myMod({"name": "World", "age":100})
Then pass it to your view as:
myview = new dashView({mymodel: A})
One more step, you have to do is to call render function:
myview.render();
Here's a complete solution:
<html>
<script src="jquery-1.10.2.min.js"></script>
<script src="underscore-min.js"></script>
<script src="backbone.js"></script>
<body>
<script type="text/template" id="dashBoardTemplate">
<p> Hello <%= name %> </p>
</script>
<div class="content-area">
</div>
<script type="text/javascript">
var myMod = Backbone.Model.extend({
defaults: {
name: "mo",
age: "10"
}
});
var dashView = Backbone.View.extend({
el: '.content-area',
template: _.template($("#dashBoardTemplate").html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
mymod = new myMod({"name": "World", "age":100});
myview = new dashView({model:mymod});
myview.render();
</script>
</body>
</html>
If you want to study backone.js, please read this open source book which get me started:
http://addyosmani.github.io/backbone-fundamentals/
You need to get properties of a Backbone Model with the getter syntax, so you need to rewrite your template to:
<p> Hello <%= obj.get('name') %> </p>
Or you need to convert your model into a plain JS object when calling _.template what you can do with the .toJSON() (which creates a clone of the model) or .attributes property:
template: _.template(dashBoardTemplate, this.mymodel.toJSON())
Side note: you should consider to move the template rendering logic into your view. Because your current code render your template when your view is declared and not when you call the render method. So you might get unexpected result. So your code you look like this:
template: _.template(dashBoardTemplate), //only compile the template
render: function() {
this.$el.html(this.template(this.mymodel.toJSON()));
return this;
}
Related
I'm using python to create a dummy server storing JSON data. I'm trying to fetch the data to display it in a dashboard. I keep getting
cannot read property html of undefined
and
cannot read property render of undefined
What am I missing?
My backbone script:
// Create a Model
var Dashboard = Backbone.Model.extend({});
// Create a collection
var DashboardCollection = Backbone.Collection.extend({
model: Dashboard,
url: 'http://localhost:8889/api/test'
});
// create an instance of the collection object
var jobList = new DashboardCollection();
jobList.fetch({success:function(){
test.render();
}});
// Create a jobList view
var jobListView= Backbone.View.extend({
el: $('.jobsList'),
template: _.template($('#test-template').html()),
initialize: function(){
this.render();
//this.listenTo(this.model, 'change', this.render);
//this.listenTo(this.model, 'destroy', this.remove);
},
render : function(){
this.$el.html(this.template({'last_name':'test'}));
return this;
}
});
var test = new jobListView;
And my HTML:
<main>
<div class="row">
<div class="left glass">
<!--[if lt IE 9]>
<div class="legacy-ie-fix"></div>
<![endif]-->
<h1>Job List</h1>
<div class ="jobsList">
</div>
</div>
<div class="right glass">
<!--[if lt IE 9]>
<div class="legacy-ie-fix"></div>
<![endif]-->
<h1>Metrics</h1>
<div id="metrics">
<div class="row">
</div>
</div>
</div>
</div>
</main>
</body>
<script type="text/template" id="test-template">
<table class="table striped">
<thead>
<tr>
<th>Data</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= last_name %></td>
</tr>
</tbody>
</table>
</script>
It seems to be an ordering problem.
Make sure the document is ready
If you use jQuery in your script to grab an element from the document (like el: $('.jobsList')), you must ensure that the HTML is ready. You can wrap your code in a jQuery style document ready function:
$(function() {
var JobListView = Backbone.View.extend({
el: $('.jobsList'),
template: _.template($('#test-template').html()),
render: function() {
this.$el.html(this.template({ 'last_name': 'test' }));
return this;
}
});
});
Or just load the scripts at the bottom of the <body> but inside of it.
<script type="text/template" id="test-template">
Put the template above the scripts loading and inside the body.
</script>
<script src="jquery.js">
<script src="underscore.js">
<script src="backbone.js">
<script src="script/my-app.js">
</body>
The order of the <script> tags on the page is important. Backbone requires jQuery and Underscore.js to be loaded before and your own code requires Backbone (and jQuery, but that's already taken care of in the dependency chain).
Declare and assign variable before using them
You call fetch on the collection, which uses the view before it is assigned. While it could work (see var hoisting), it's best to declare and assign variables before using them when possible.
// Create a list view class
var JobListView = Backbone.View.extend({
el: '.jobsList', // no need to use jQuery here.
template: _.template($('#test-template').html()),
render: function() {
this.$el.html(this.template({ 'last_name': 'test' }));
return this;
}
});
// instantiate the view first.
var test = new JobListView();
// then create an instance of the collection object
var jobList = new DashboardCollection();
// and fetch it when everything is ready.
jobList.fetch({
success: function() {
test.render();
}
});
Notice that JS custom types (classes) should be in PascalCase rather than in snakeCase as a generally approved standard, but that's not going to make the code fail.
Pass the element to the view
To be able to easily reuse your views within different views and templates, you should avoid hard-coding the el property.
Instead, pass the element to the view:
var JobListView = Backbone.View.extend({
// ...
});
// ...somewhere else
var view = new JobListView({ el: '.jobsList' });
Or use the element created by a Backbone view.
var JobListView = Backbone.View.extend({
className: 'jobList',
});
// ...inside a parent view's render
var ParentView = Backbone.View.extend({
template: '<div class="job-list-1"></div><div class="job-list-2"></div>',
render: function() {
this.$el.html(this.template);
this.$('.job-list-1').html(new JobListView().render().el);
this.$('.job-list-2').html(new JobListView().render().el);
// ...
return this;
}
});
This would result in:
<div class="job-list-1">
<div class="jobList"></div>
</div>
<div class="job-list-2">
<div class="jobList"></div>
</div>
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.
I'm new to backbone and trying to make a book library app. While running this code, it is not showing the template.
This is my index.html
<html>
<head>
<title>Example</title>
</head>
<body>
<form>
Name:<input type='text' id='name'/><br>
Author:<input type='text' id='auth'/><br>
Keyword:<input type='text' id='keyword'/><br><br>
<button id="add">Add</button>
</form>
<div id='book_list'>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.io/backbone/backbone-min.js"></script>
<script src="script.js"></script>
<script id="bookTemplate" type="text/template">
<ul>
<li><%= name %></li>
<li><%= auth %></li>
<li><%= keyword %></li>
</ul>
<button class="delete">Delete</button>
</script>
</body>
</html>
This is script.js
$(function(){
var bookmodel = Backbone.Model.extend({
defaults: {
name:'temp',
auth:'meee',
keyword:'nonee'
},
});
var booklist = Backbone.Collection.extend({
model:bookmodel
});
var bookview= Backbone.View.extend({
tagName:'div',
className: 'bookContainer',
template: _.template( $('#bookTemplate').html()),
events:{
'click .delete':'deleteBook'
},
render : function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
},
deleteBook: function(){
this.model.destroy();
this.remove();
}
});
var library = Backbone.View.extend({
model: bookmodel,
initialize: function( initialBooks ) {
$el='#book_list';
var one=new bookmodel({name:'ankur 1',auth:'asdf 1',keyword:'asdfkasdf 1'});
var two=new bookmodel({name:'ankur 2',auth:'asdf 2',keyword:'asdfkasdf 2'});
var bookcoll= [one,two];
this.collection = new booklist(bookcoll);
this.render();
},
render:function(){
this.collection.each(function (item){
var k= new bookview({model:item});
this.$el.append(k.render().el);
},this);
},
});
var xyz= new library();
})
Also, when i'm trying to code like this:
var library = Backbone.View.extend({
model: bookmodel,
$el:'#book_list';
..... //rest of the code
)};
var xyz= new library();
Then,it is leading to : Uncaught TypeError: undefined is not a function, at line
var xyz= new library();
I ran your code and it seemed fine. I dont know exactly whats in script.js but try including your template above your script.js file. It probably can't find your template at the point it was running
I was able to recreate your error in jsfiddle by using their underscore library backbone loader. It wasn't an issue with your code. The following fiddle shows your same error:
http://jsfiddle.net/32tsA/
While this one works fine:
http://jsfiddle.net/BympL/
The issue was with how you had the fiddle set up in my estimation.
I did make some minor changes to fix up capitalization and some best practices with Backbone:
var Bookmodel = Backbone.Model.extend({
defaults: {
name:'temp',
auth:'meee',
keyword:'nonee'
}
});
var Booklist = Backbone.Collection.extend({
model: Bookmodel
});
var Bookview = Backbone.View.extend({
tagName:'div',
className: 'bookContainer',
template: _.template( $('#bookTemplate').html()),
events:{
'click .delete':'deleteBook'
},
render : function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
},
deleteBook: function(){
this.model.destroy();
this.remove();
}
});
var one=new Bookmodel({name:'ankur 1',auth:'asdf 1',keyword:'asdfkasdf 1'});
var two=new Bookmodel({name:'ankur 2',auth:'asdf 2',keyword:'asdfkasdf 2'});
var bookcoll = [one,two];
var mybooks = new Booklist(bookcoll);
var Library = Backbone.View.extend({
render:function(){
this.collection.each(function (item){
var k= new Bookview({model:item});
this.$el.append(k.render().el);
},this);
},
});
var xyz = new Library({collection: mybooks, el: "#book_list"});
xyz.render();
I named the classes with capital case, removed the initialization of the models from your view (views should be told their models not create their models), and abstracted the el declaration from the Library declaration (so you can reuse the view in a different place).
i am trying to learn backbone.js ( Backbone.js 1.0.0) this is my sample html page where iam using collection. fetch() method to get the collection,and it is displayed using view .i am getting result in
google chrome,but nothing is displayed in mozilla. i don't know the exact reason.
while i refere to backone site http://backbonejs.org/#Collection-fetch
it is qouted that :
Note that fetch should not be used to populate collections on page load — all models needed at load time should already be bootstrapped in to place. fetch is intended for lazily-loading models for interfaces that are not needed immediately: for example, documents with collections of notes that may be toggled open and closed.
is this is related with my issue?
this is my sample html page
<!DOCTYPE html>
<html>
<head>
<title>Backbone Application</title>
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/underscore.js" type="text/javascript"></script>
<script src="js/backbone.js" type="text/javascript"></script>
</head>
<body>
<div class="list"></div>
<script id="personTemplate" type="text/template">
<td> <strong><%= name %></strong></td>
<td>(<%= age %>) </td>
<td> <%= occupation %> </td>
</script>
<script type="text/javascript">
//Person Model
var Person = Backbone.Model.extend({
defaults: {
name: 'Guest User',
age: 30,
occupation: 'worker'
}
});
// A List of People
var PeopleCollection = Backbone.Collection.extend({
model: Person,
initialize: function(){
alert("intialise")
},
url:'/RestFul/rest/members/info',
});
// View for all people
var PeopleView = Backbone.View.extend({
tagName: 'table',
render: function(){
this.collection.each(function(person){
var personView = new PersonView({ model: person });
this.$el.append(personView.render().el); // calling render method manually..
}, this);
return this; // returning this for chaining..
}
});
// The View for a Person
var PersonView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#personTemplate').html()),
////////// initialize function is gone from there. So we need to call render method manually now..
render: function(){
this.$el.html( this.template(this.model.toJSON()));
return this; // returning this from render method..
}
});
var peopleCollection = new PeopleCollection();
//peopleCollection.fetch();
peopleCollection.fetch({ success: function () { console.log("collection fetched"); } });
//peopleCollection.fetch({context:collection}).done(function() {
// console.log(this.length)
// })
//console.log(peopleCollection.toJSON())
alert(JSON.stringify(peopleCollection));
var peopleView = new PeopleView({ collection: peopleCollection });
$(document.body).append(peopleView.render().el); // adding people view in DOM
</script>
</body>
</html>
any help will be appreciated
Try with
var fetching = peopleCollection.fetch({ success: function () { console.log("collection fetched"); } });
$.when(fetching).done(function(){
var peopleView = new PeopleView({ collection: peopleCollection });
$(document.body).append(peopleView.render().el); // adding people view in DOM
});
var fetching = peopleCollection.fetch({ success: function () {
var peopleView = new PeopleView({ collection: peopleCollection });
$(document.body).append(peopleView.render().el);
} });
I think we can call the view render inside the success callback
I'm trying to achieve to render a block of HTML by using a model's attribute as a variable in a view.
App = Backbone.Model.extend({
contentType: "text"
});
AppView = Backbone.View.extend({
ContentTpl: _.template( $('#slide-'+this.model.get('contentType')).html() ),
render: function(){
$('#wrapper').html( this.ContentTpl() );
}
});
var app = new App();
var appView = new AppView({model: app});
appView.render();
HTML:
<div id="wrapper">
</div>
<script type="text/template" id="slide-text">
<p>This is a test</p>
</scrip>
But this results in an error...
You defined your view wrong.
you have
AppView = Backbone.Model.extend({
it should be
AppView = Backbone.View.extend({
and, you cannot evaluate this.model before view is initialized, do the following instead:
ContentTpl: function() {
return _.template( $('#slide-'+ this.model.contentType).html() );
},
and , contentType is not a model attribute, it's an attribute on the model object, you cannot use get().
to define it as a model attribute you have to either define it as a default value in the model, or pass it when you instantiate an object:
var app = new App({contentType: 'text'});
or
App = Backbone.Model.extend({
defaults: {
"contentType": "text"
}
});
You have to load your template in the initialize function. this.model doesn't exist at that point.
initialize: function() {
this.ContentTpl = _.template( $('#slide-'+this.model.get('contentType')).html() );
}
However, this is still a bad pattern form backbone. I would inject it as an option.
var template = $('#slide-'+this.model.get('contentType')).html();
var appView = new AppView({model: app, ContentTpl: template });
...
// in your view
initialize: function(options) {
this.ContentTpl = _.template( options.ContentTpl );
}