I am new to backbone underscore and require.js.
I followed this tutorial to create a project using backbone.js and underscore.js .
Then I want to add require.js to that project.
This is what I modify in the theater.html :
<body>
<h1>My Theater</h1>
<script src="libs/require.js" type="text/javascript"></script>
<script src="main.js" type="text/javascript"></script>
<div id="mainContainer"></div>
<input type="button" value="Add Item" id="butAddItem" />
<script type="text/template" id="tmplt-Movies">
<ul>
</ul>
</script>
<script type="text/template" id="tmplt-Movie">
<div>*******************************************************</div>
<div><%= Id %> </div>
<div><%= Name %> </div>
<div><%= AverageRating %> </div>
<div><%= ReleaseYear %> </div>
<div><%= Url %> </div>
<div><%= Rating %> </div>
</script>
</body>
I added fews line of code to main.js file :
require.config({
paths: {
jquery: 'libs/jquery-1.7.1',
underscore: 'libs/underscore',
backbone: 'libs/backbone'
} });
Then I got 2 errors :
1. ReferenceError: Backbone is not defined , Theater.Models.Movie = Backbone.Model.extend({});
This is main.js file :
require.config({
paths: {
jquery: 'libs/jquery-1.7.1',
underscore: 'libs/underscore',
backbone: 'libs/backbone'
} });
var Theater = {
Models: {},
Collections: {},
Views: {},
Templates:{}
};
Theater.Models.Movie = Backbone.Model.extend({});
Theater.Collections.Movies = Backbone.Collection.extend({
model: Theater.Models.Movie,
url: "data/movies.json",
initialize: function(){
console.log("Movies initialize");
}
});
Theater.Templates.movies = _.template($("#tmplt-Movies").html());
Theater.Views.Movies = Backbone.View.extend({
el: $("#mainContainer"),
template: Theater.Templates.movies,
//collection: new Theater.Collections.Movies(), //Not needed
initialize: function () {
//_.bindAll(this, "render", "addOne", "addAll");
this.collection.bind("reset", this.render, this);
this.collection.bind("add", this.addOne, this);
},
render: function () {
console.log("render");
console.log(this.collection.length);
$(this.el).html(this.template());
this.addAll();
},
addAll: function () {
console.log("addAll");
this.collection.each(this.addOne);
},
addOne: function (model) {
console.log("addOne");
view = new Theater.Views.Movie({ model: model });
$("ul", this.el).append(view.render());
}
});
Theater.Templates.movie = _.template($("#tmplt-Movie").html());
Theater.Views.Movie = Backbone.View.extend({
tagName: "li",
template: Theater.Templates.movie,
//events: { "click .delete": "test" },
initialize: function () {
//_.bindAll(this, 'render', 'test');
this.model.bind('destroy', this.destroyItem, this);
this.model.bind('remove', this.removeItem, this);
},
render: function () {
return $(this.el).append(this.template(this.model.toJSON())) ;
},
removeItem: function (model) {
console.log("Remove - " + model.get("Name"));
this.remove();
}
});
Theater.Router = Backbone.Router.extend({
routes: {
"": "defaultRoute" //http://localhost:22257/Theater/theater.htm
},
defaultRoute: function () {
console.log("defaultRoute");
Theater.movies = new Theater.Collections.Movies();
new Theater.Views.Movies({ collection: Theater.movies }); //Add this line
Theater.movies.fetch();
console.log(Theater.movies.length);
}
});
var appRouter = new Theater.Router();
Backbone.history.start();
//This is a hack for demonstration purposes
$("#butAddItem").click(null, function () {
var movie = new Theater.Models.Movie(
{
"Id": "BVP3s",
"Name": "Lord of the Rings: The Return of the King: Extended Edition: Bonus Material",
"AverageRating": 4.3,
"ReleaseYear": 2003,
"Url": "http://www.netflix.com/Movie/Lord_of_the_Rings_The_Return_of_the_King_Extended_Edition_Bonus_Material/70024204",
"Rating": "PG-13"
}
);
Theater.movies.add(movie);
console.log(Theater.movies.length);
});
And i have no idea how to convert the main.js and create a app.js file for using require.js.
Any idea please.
Thank you so much.
Firstly...
<script src="libs/require.js" type="text/javascript"></script>
<script src="main.js" type="text/javascript"></script>
Could be
<script src="libs/require.js" data-main="main.js" type="text/javascript"></script>
Secondly
require.config({
baseUrl: '.',
shim: {
'backbone': {
deps: ['underscore'],
exports: 'Backbone'
}
},
deps: ['backbone','jquery'],
paths: {
jquery: 'libs/jquery-1.7.1',
underscore: 'libs/underscore',
backbone: 'libs/backbone'
}
});
require(['app']);
And finally wrap your app.js in a define.
define(function () {
Theater.Models.Movie = Backbone.Model.extend({});
Theater.Collections.Movies = Backbone.Collection.extend({
model: Theater.Models.Movie,
url: "data/movies.json",
initialize: function () {
console.log("Movies initialize");
}
});
Theater.Templates.movies = _.template($("#tmplt-Movies").html());
Theater.Views.Movies = Backbone.View.extend({
el: $("#mainContainer"),
template: Theater.Templates.movies,
//collection: new Theater.Collections.Movies(), //Not needed
initialize: function () {
//_.bindAll(this, "render", "addOne", "addAll");
this.collection.bind("reset", this.render, this);
this.collection.bind("add", this.addOne, this);
},
render: function () {
console.log("render");
console.log(this.collection.length);
$(this.el).html(this.template());
this.addAll();
},
addAll: function () {
console.log("addAll");
this.collection.each(this.addOne);
},
addOne: function (model) {
console.log("addOne");
view = new Theater.Views.Movie({
model: model
});
$("ul", this.el).append(view.render());
}
});
Theater.Templates.movie = _.template($("#tmplt-Movie").html());
Theater.Views.Movie = Backbone.View.extend({
tagName: "li",
template: Theater.Templates.movie,
//events: { "click .delete": "test" },
initialize: function () {
//_.bindAll(this, 'render', 'test');
this.model.bind('destroy', this.destroyItem, this);
this.model.bind('remove', this.removeItem, this);
},
render: function () {
return $(this.el).append(this.template(this.model.toJSON()));
},
removeItem: function (model) {
console.log("Remove - " + model.get("Name"));
this.remove();
}
});
Theater.Router = Backbone.Router.extend({
routes: {
"": "defaultRoute" //http://localhost:22257/Theater/theater.htm
},
defaultRoute: function () {
console.log("defaultRoute");
Theater.movies = new Theater.Collections.Movies();
new Theater.Views.Movies({
collection: Theater.movies
}); //Add this line
Theater.movies.fetch();
console.log(Theater.movies.length);
}
});
var appRouter = new Theater.Router();
Backbone.history.start();
//This is a hack for demonstration purposes
$("#butAddItem").click(null, function () {
var movie = new Theater.Models.Movie({
"Id": "BVP3s",
"Name": "Lord of the Rings: The Return of the King: Extended Edition: Bonus Material",
"AverageRating": 4.3,
"ReleaseYear": 2003,
"Url": "http://www.netflix.com/Movie/Lord_of_the_Rings_The_Return_of_the_King_Extended_Edition_Bonus_Material/70024204",
"Rating": "PG-13"
});
Theater.movies.add(movie);
console.log(Theater.movies.length);
});
});
You could switch out your version of backbone for backbone-amd which is a AMD compatible version available through Bower, and use Lodash instead of Underscore. On top of that, you should start thinking about abstracting your backbone models, collections, views and router into separate files.
Hope this helps.
Related
Hi I've this function in my router:
home: function() {
var attore= new Attore();
var page = new ListPostView({
model: this.attore
});
this.changePage(page);
},
and the model Attore is:
var Attore = Backbone.Model.extend({
defaults: {
"nome": "caesar salad",
"cognome": "ravioli"
},
});
return Attore;
});
but happens an error that tell me model is undefined in the view:
var ListPostView = Backbone.View.extend({
tagName: "ul",
id: "list",
template: Handlebars.compile(template),
initialize: function () {
console.log(attore);
this.model.bind("reset", this.render, this);
},
render: function (eventName) {
$(this.el).empty();
_.each(this.model.models, function (ad) {
$(this.el).append(new SinglePostView({
model: ad
}).render().el);
}, this);
return this;
}
});
return ListPostView;
});
Array Of Objects
The data is received from server
var Updates = [
{"post_id":"1","post_desc":"This is my first post",
"comments":[{"id":1,"comment":"some comments","like":7},
{"id":9,"comment":"some comments","like":3}
]
},
{"post_id":"2","post_desc":"This is my second post",
"comments":[{"id":5,"comment":"some comments","like":5}]
}]
Model:
var Update = Backbone.Model.extend({
defaults:{
photo: "default.png"
}
});
Collection:
var latestUpdates = Backbone.Collection.extend({
model: Update
});
Single View:
var UpdateView = Backbone.View.extend({
tagName: "div",
className: "post-container",
template: $("#postTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
Master view:
var UpdatesView = Backbone.View.extend({
el: $("#postContainer"),
initialize: function () {
this.collection = new latestUpdates(Updates);
this.render();
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.renderUpdates(item);
}, this);
},
renderUpdates: function (item) {
var updateView = new UpdateView({
model: item
});
this.$el.append(updateView.render().el);
}
});
//create app instance
var wallUpdates = new UpdatesView();
How can I render comments section under each post?
Trying to achieve layout similar to facebook post-comment system
I'd use a CommentListView, owned by your UpdateView. tagName: "ul", className: "post-comments"
Then have a CommentView owned by the CommentListView. CommentView's render should not append anything to the DOM, but return its $el.
CommentListView would tell each of the CommentView's to render, appending each of their $el's to the CommentListView's $el.
For the containers, I'd use:
<div class="post-container" data-post-id="<%= YourPostId %>">
<div class="post-body">
<!--Your post can go in here-->
</div>
<ul class="post-comments">
<!--Append your comments in here-->
</ul>
</div>
I working on a project to test this backbone-boilerplate (https://github.com/tbranyen/backbone-boilerplate) but i have a problem to render a view.
I have the first call to beforeRender, the collection (pics) is empty because the fetching of Flickr photos is still in progress.
But when the data are loaded, i can see them in the "parse" function of my Pics.Collection (with a breakpoint), the beforeRender of my view is never called again.
My router.js :
define([
// Application.
"app",
"modules/pics"
],
function(app, HomePage, Pics) {
// Defining the application router, you can attach sub routers here.
var Router = Backbone.Router.extend({
initialize: function() {
// Init collections
var collections = {
pics: new Pics.Collection()
};
console.log('collec');
// Register in the router
_.extend(this, collections);
},
routes: {
"": "pics"
},
pics: function() {
this.reset();
app.useLayout().setViews({
"#content": new Pics.Views.List({ pics: this.pics }),
"header": new Backbone.View({
tagName: "span",
beforeRender: function() {
this.$el.html("Header Pics");
}
})
}).render();
this.pics.fetch();
},
reset: function() {
if (this.pics.length) {
this.pics.reset();
}
}
});
return Router;
});
My module, pics.js :
define([
"app"
], function(app) {
var Pics = app.module();
Pics.Model = Backbone.Model.extend({ });
Pics.Collection = Backbone.Collection.extend({
model: Pics.Model,
url: function() {
return "http://api.flickr.com/services/feeds/photos_public.gne?format=json&jsoncallback=?";
},
cache: true,
parse: function(obj) {
return obj.items;
}
});
Pics.Views.Item = Backbone.View.extend({
template: "pics/item",
tagName: "li",
serialize: function() {
return { model: this.model };
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
}
});
Pics.Views.List = Backbone.View.extend({
template: "pics/list",
serialize: function() {
return { collection: this.options.pics };
},
beforeRender: function() {
this.options.pics.each(function(pic) {
this.insertView("#pics-list", new Pics.Views.Item({
model: pic
}));
}, this);
},
initialize: function() {
this.listenTo(this.options.pics, {
"reset": this.render(),
"request": function() {
this.$("ul").parent().html("<img src='/app/img/spinner-gray.gif'>");
}
});
}
});
return Pics;
});
list.html (template) :
<h1>Liste des photos</h1>
<div>
<div class="loading"></div>
<ul id="pics-list"></ul>
</div>
item.html (template) :
<li>
<img src="<%= model.get("link") %>" />
</li>
index.html :
<main role="main" id="main">
<nav>
<ul>
<li>Home</li>
</ul>
</nav>
<header></header>
<aside></aside>
<section id="content"></section>
<footer>
Copyright 2013
</footer>
</main>
I search if an existing issue exists, on forums and by myself since two days but i can't find what is going wrong.
Thanks in advance, Pierrick.
I found the problem, listen the "sync" event instead of "change".
How do I pass the name variable from the router to the FriendInfocollection? Anytime I browse to localhost/app/person/MyName console.log returns a 404 for localhost/slim/index.php/person/
Can someone point me in the right direction on sharing this variable with the collection so it uses the correct url?
<!DOCTYPE html>
<html>
<head>
<title>I have a back bone</title>
</head>
<body>
<button id="add-friend">Add Friend</button>
<button id='view-friends'>View Friends</button>
<ul id="friends-list">
</ul>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<script>
Friend = Backbone.Model.extend({
name: null,
age: null,
});
FriendDetailModel = Backbone.Model.extend();
FriendInfoModel = Backbone.Model.extend({
name: null
});
FriendDetailCollection = Backbone.Collection.extend({
url: '../slim/index.php/people/',
model: FriendDetailModel
});
FriendInfoCollection = Backbone.Collection.extend({
model: FriendInfoModel,
url: '../slim/index.php/person/' + name,
});
Friends = Backbone.Collection.extend({
initialize: function(models, options) {
this.bind("add",options.view.addFriendLi);
}
});
AppView = Backbone.View.extend({
el: $("body"),
initialize: function() {
this.friends= new Friends(null, {view:this});
},
events: {
"click #add-friend": "showPrompt",
},
showPrompt: function () {
var friend_name = prompt("Who is your friend?");
var friend_age = prompt("What is your friends age?");
var friend_model = new Friend({name: friend_name, age: friend_age});
this.friends.add(friend_model);
},
addFriendLi: function(model) {
$("#friends-list").append("<li>" + model.get('name') + " " + model.get('age') + "</li>");
}
});
var appview = new AppView;
var people = new FriendDetailCollection;
var person = new FriendInfoCollection({name:'Sean'});
FriendView = Backbone.View.extend({
el: $("body"),
initialize: function() {
_.bindAll(this,'render');
this.collection.bind('reset', this.render);
},
events: {
"click #view-friends": "fetch_list",
},
render: function() {
var results = this.collection.toJSON();
$.each(results, function(key, value) {
var msg = results[key]['firstname'] + " " + results[key]['lastname'];
console.log(msg);
});
},
fetch_list: function() {
people.fetch({
success: function(data) {
//console.log("success");
}
});
}
});
FriendInfoView = Backbone.View.extend({
el: $("body"),
initialize: function(name) {
_.bindAll(this,'render');
this.collection.bind('reset', this.render);
},
render: function(name) {
var results = this.collection.toJSON();
$.each(results, function(key, value) {
var msg = results[key]['firstname'] + " " + results[key]['lastname'];
console.log(msg);
});
}
});
friendview = new FriendView({collection: people});
friendinfoview = new FriendInfoView({collection: person});
AppRouter = Backbone.Router.extend({
routes: {
"friends":"people",
"person/:name":"personDetail"
},
people: function() {
console.log('all the people');
people.fetch({
success: function(data) {
//console.log("success");
}
});
},
personDetail: function(name) {
person.fetch({
success: function(data) {
console.log("success");
}
});
console.log('one person named ' + name);
}
});
var approuter = new AppRouter;
Backbone.history.start();
</script>
</body>
</html>
You should define routers like this :
var AppRouter = Backbone.Router.extend({
routes: {
'name/:name' : 'getPerson'
},
getPerson: function(name) {
var callperson = new Person.Details({
name : name
});
}
Instantiate routes when dom ready
app_router = new AppRouter;
Now you can access the name like this on your view:
this.options.name
But you must define the options variable inside the initialize functions
initialize: function ( options )
And for sure you can set the model like this :
model.set('name', name)
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.