I'm new to Backbone and trying to put together a small app and having problems getting a view to render client side.
Here is my client html in jade.
extends layout
block content
.row
#breadcrumbs.span12
script#room-list-template(type="text/template")
<td><%=name%></td>
<td><button class="btn btn-info">Join Room</button></td>
script(src="/javascripts/dislocated_poker/index.js").
script(src="/javascripts/dislocated_poker/nav.js").
script(src="/javascripts/dislocated_poker/room.js").
script(type="text/javascript").
$(function(){
DislocatedPoker.init();
})
This call my init function to fetch the data which is stashed away in MongoDb
DislocatedPoker = {
init : function() {
var crumbView = new DislocatedPoker.BreadcrumbView({el : "#breadcrumbs"});
crumbView.render();
var rooms = new DislocatedPoker.Rooms();
var roomListView = new DislocatedPoker.RoomListView({collection : rooms});
rooms.fetch();
}
};
And here are my views and models.
DislocatedPoker.Room = Backbone.Model.extend({
});
DislocatedPoker.Rooms = Backbone.Collection.extend({
model : DislocatedPoker.Room,
url : "/api/rooms"
});
DislocatedPoker.RoomView = Backbone.View.extend({
tagName : "tr",
render : function() {
var template = $("#room-list-template").html();
var compiled = _.template(template, this.model.toJSON());
$(this.el).html(compiled);
return this;
}
})
DislocatedPoker.RoomListView = Backbone.View.extend({
initialize : function() {
this.collection.bind("reset", this.render, this);
this.collection.bind("add", this.render, this);
},
tagName : "table",
className : "table table-striped",
render : function() {
var els = [];
this.collection.each(function(item) {
var itemView = new DislocatedPoker.RoomView({model : item});
els.push(itemView.render().el);
})
//return this;
$(this.el).html(els);
$("#room-list").html(this.el);
}
})
I see JSON being returned from the fetch() method and the collection is iterated, but the result never ends up as client html. If I view the source of the HTML I see the following where the template should render.
<script id="room-list-template" type="text/template"><td><%=name%></td>
<td><button class="btn btn-info">Join Room</button></td>
I feel like I am missing something pretty obvious but can't seem to pinpoint the issue.
Any guidance is much appreciated.
Thanks.
It looks like the following won't work:
$(this.el).html(els);
jQuery's html function takes a string, you're providing an array. Try with:
$(this.el).html(els.join(""));
You should try the following:
this.collection.bind("fetched", this.render, this);
Related
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 doing a sample work to render the element using the data. but I am not getting my result. what is wrong here?
any one help me?
here is my code: please go for fiddle for the live demo.
html:
<div id="content" class="content"></div>
<script type="text/template" id="list">
<%= name %>
</script>
Javascript:
var data = [
{"name":"name1","city":"city1","age":"age1","subModel":[
{"name":"name01","city":"city01","age":"age01","subModel":[
{"name":"name001","city":"city001","age":"age001"}
]},
{"name":"name02","city":"city02","age":"age02","subModel":[
{"name":"name002","city":"city002","age":"age002"}
]},
{"name":"name03","city":"city03","age":"age03","subModel":[
{"name":"name003","city":"city003","age":"age003"}
]}
]}
];
var myApp = new Backbone.Marionette.Application();
myApp.addRegions({
mainRegion:"#content"
});
var myModel = Backbone.Model.extend({
defaults:{
"name":"no name",
"city":"chennai",
"age":"10 months"
}
});
var myCollection = Backbone.Collection.extend({
model:myModel
});
var oneView = Backbone.Marionette.ItemView.extend({
tagName:"li",
template:"#list"
});
var multiView = Backbone.Marionette.CompositeView.extend({
tagName:'ul',
itemView:oneView
});
myApp.on('initialize:after', function(){
var listView = new multiView({collection:data});
myApp.mainRegion.show(listView);
});
myApp.start();
Live Demo
Here you go: http://jsfiddle.net/Cardiff/Y5kwM/
There are several errors here:
var multiView = Backbone.Marionette.CompositeView.extend({
tagName:'ul',
itemView:oneView
});
You need to provide a template to a compositeView, otherwise use a collectionview.
var listView = new multiView({collection:data});
You can't just set an array as a collection. You need a valid backbone collection (see fiddle).
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 use Web Api and Knockout.js in my project. I want to try like this: if I click the "Home" I want to refresh just main div. So I write this code.
My script in layout.cshtml
<script type="text/javascript">
$(document).ready(function () {
ko.applyBindings(new TalesViewModel());//First load the code is runnig and load the main div
function TalesViewModel() {
var self = this;
self.tales = ko.observableArray();
$.getJSON("/api/tales/", self.tales);
}
$('#home').click(function () {
var Tale = function (TaleName, Content, VoicePath, Tales) {
self = this;
self.TaleName = TaleName;
self.Content = Content;
self.VoicePath = VoicePath;
}
var mapping = {
'tales': {
create: function (options) {
return new Tale(options.data.TaleName, options.data.Content,
options.data.VoicePath);
}
}
}
var data = $.getJSON("/api/tales/", Tale);
var viewModel = ko.mapping.fromjs(data, mapping);
ko.applyBindings(viewModel);
})
})
</script>
I want to refresh this place
<div id="main">
#RenderBody()
</div>
TaleList.cshtml (PartialView)
<div>
<ul data-bind="foreach: tales">
<li>
<div>
<div>Masal Adı</div>
<span data-bind="text: $data.TaleName"></span>
</div>
<div>
<div>İçerik</div>
<span data-bind="text: $data.Content"></span>
</div>
<div>
<div>Ses Dosyası</div>
<span data-bind="text: $data.VoicePath"></span>
</div>
</li>
</ul>
When I clicked Home main div is refresh but no data in here. I think I have to use Knockout something but I don't know how can I do it.
I hope I can explain. Thanks all replies.
Update
If I check with firebug I see this error "TypeError: Object # has no method 'fromjs'"
Update2
I added my first knockout code when I load the project.
This is what you need to do:
Create a js object
var Tale = function (TaleName, Content, VoicePath, Tales) {
self = this;
self.TaleName = TaleName;
self.Content = Content;
self.VoicePath = VoicePath;
}
Create a mapping to convert to your js objects
var mapping = {
'tales': {
create: function(options) {
return new Tale(options.data.TaleName, options.data.Content,
options.data.VoicePath);
}
}
}
Check that your data matches something like below, checking the names match as below:
var data = {"tales" : [{"TaleName": "T1", "Content":"c1", "VoicePath":"v1"}, {"TaleName": "T2", "Content":"c2", "VoicePath":"v2"}]}
var viewModel = ko.mapping.fromJS(data, mapping);
Apply the bindings
ko.applyBindings(viewModel);
Here is a working fiddle with mimicked data
http://jsfiddle.net/dxJpc/1/
Update
You are mixing a combination of getJson and ajax, you only need one.
This can be replaced: (With Ajax)
$.ajax({
type: 'GET',
url: '/Pages/TaleList/',
contentType: 'application/html; charset=utf-8',
dataType: 'html'
})
.success(function (data) {
alert("okey!")
var viewModel = ko.mapping.fromJS(data, mapping);
ko.applyBindings(viewModel);
})
.error(function (req, status, error) {
alert("Error!Occured")
})
With getJSON:
var data = $.getJSON("/api/tales/", Tale);
var viewModel = ko.mapping.fromJS(data, mapping);
ko.applyBindings(viewModel);
Update 3
If you are loading your initial load as you have changed it to, you can simply put this in your on click event:
$('#home').click(function () {
ko.applyBindings(new TalesViewModel());
})
Update 4
Declare the view model in the document ready.
$(document).ready(function () {
var viewModel = new TalesViewModel();
ko.applyBindings(viewModel);
Then change your click to this:
$(document).ready(function () {
viewModel = new TalesViewModel();
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 );
}