Backbone render with collection - backbone.js

I'm starting to study Backbone with requirejs
I follow some tutorial online and now I have a little doubt.
When I fetch data from a Json print me into the console log too much line, if into my json there is 4 element, it print me 4*4 element.
This is my code:
Collection:
define(['backbone', 'models/todo'], function(Backbone, TodoModel){
var todoCollection = Backbone.Collection.extend({
model: TodoModel,
url:'json/todos.json',
parse: function(data){
return data.result;
}
});
return todoCollection;
});
App:
define(['jquery' , 'backbone', 'views/todo', 'models/todo', 'collections/todo'],
function($, Backbone, TodoView, TodoModel, TodoCollection){
var AppView = Backbone.View.extend({
el:$('#placeholder'),
initialize: function(){
console.log('initialize AppView');
this.todos = new TodoCollection();
this.todos.bind('all',this.render, this);
this.todos.fetch();
},
render: function(){
console.log('Data is fetched');
this.todos.each(function(model){
console.log(model.get("content"));
});
}
})
return AppView;
});
Json:
{
"result":[
{
"content" : "Todo1"
},
{
"content" : "Todo2"
},
{
"content" : "Todo3"
},
{
"content" : "Todo4"
}
]
}
Output into the console:
initialize AppView app.js:8
Data is fetched app.js:14
4
Initialized Todo model todo.js:7
Data is fetched app.js:14
Todo1 app.js:16
Todo2 app.js:16
Todo3 app.js:16
Todo4 app.js:16
Data is fetched app.js:14
Todo1 app.js:16
Todo2 app.js:16
Todo3 app.js:16
Todo4 app.js:16
Data is fetched app.js:14
Todo1 app.js:16
Todo2 app.js:16
Todo3 app.js:16
Todo4 app.js:16
Data is fetched app.js:14
Todo1 app.js:16
Todo2 app.js:16
Todo3 app.js:16
Todo4 app.js:16
Data is fetched app.js:14
Todo1 app.js:16
Todo2 app.js:16
Todo3 app.js:16
Todo4 app.js:16
Why there is so much lines?
I expected an output like this:
initialize AppView app.js:8
Data is fetched app.js:14
4
Initialized Todo model todo.js:7
Data is fetched app.js:14
Todo1 app.js:16
Todo2 app.js:16
Todo3 app.js:16
Todo4 app.js:16
I don't understand the point. Can someone explain what am I wrong?
Thanks

Your problem is here:
this.todos.bind('all',this.render, this);
You are binding to all events which in turn calls this.render several times.
Try this instead:
this.todos.bind('sync', this.render, this);
or, better yet:
this.todos.on('sync', this.render, this);

Related

fetch collection using rest api in backbone

In that rest api url, i am getting an json array and have fetch it with EmployeeList collection. using the fetch() only call the rest api. If I didnt use the fetch, the rest api call doesnt work, it tested using the log in api-code. while fetching I get all the details, but I am getting the error,
Uncaught TypeError: this.model is not a constructor backbone-min.js:24
I am new to the backbonejs. Whats the error, why this error will happens. My code is below,
var app = {};
app.Employee = Backbone.Model.extend();
app.employee = new app.Employee();
app.EmployeeList = Backbone.Collection.extend({
model: app.employee,
url:'/api/employ',
parse: function(response) {
return response;
}
})
app.employeeList = new app.EmployeeList();
app.employeeList.fetch();
app.AppView = Backbone.View.extend({
el: '#emp',
initialize: function(){
this.render();
console.log(app.employeeList);
},
render: function(){
this.$el.html('sathish');
}
});
app.appView = new app.AppView();
Change
app.EmployeeList = Backbone.Collection.extend({
model: app.employee,
.....
})
To
app.EmployeeList = Backbone.Collection.extend({
model: app.Employee,
...
})
the model param should be a model class(a constructor function in js) like app.Employee.
You should provide app.Employee() instead of new app.Employee() as a model parameter of the collection.

Backbone call a model method from the view

I have a backbone app with require where I want to add a collection inside a collection with a method inside model.
I have tried to insert the method in the collection but I can't add elements.
I'd want to make a collection of app when I click an element outside the app I want add inside my app other app in a collection.
This is my app:
Model:
define(['backbone', 'collections/element'],function(Backbone, ElementCollection){
var DesignModel = Backbone.Model.extend({
initialize:function(){
console.log('Initialized Design model');
_.defaults(this, {
elements: new ElementCollection()
});
},
addElement: function(elements, options) {
return this.elements.add(elements, options);
}
});
return DesignModel;
});
Collection:
define(['backbone', 'models/design'], function(Backbone, DesignModel){
var designCollection = Backbone.Collection.extend({
model: DesignModel,
});
return designCollection;
});
View
define(['jquery' , 'backbone', 'models/design', 'collections/design', 'views/element'],
function($, Backbone, DesignModel, DesignCollection, ElementView){
var DesignView = Backbone.View.extend({
el:$('#page'),
initialize: function(){
console.log('initialize DesignView');
this.collection = new DesignCollection();
var here = this;
$('#insert-dynamic-element').click(function(){
var element = new ElementView();
here.collection.models.addElement(element);
});
},
render: function(){
}
})
return DesignView;
});
I have tried to call the function addElement in this way:
here.collection.models.addElement(element);
and
here.collection.addElement(element);
But always with error that Object has no method addElement
How can I solve this? I want to call the method addElement from the view to add an app inside another app in a collection.
Thanks
The safest way to call the method is to add the method to the collection instead of the Model. Currently the method is available on the Model instance .
So this.collection.models.addElement will not cut it
Collection
define(['backbone', 'models/design'], function(Backbone, DesignModel){
var designCollection = Backbone.Collection.extend({
model: DesignModel,
addElement: function(elements, options) {
return this.add(elements, options);
}
});
return designCollection;
});
View
define(['jquery' , 'backbone', 'models/design', 'collections/design', 'views/element'],
function($, Backbone, DesignModel, DesignCollection, ElementView){
var DesignView = Backbone.View.extend({
el:$('#page'),
initialize: function(){
console.log('initialize DesignView');
this.collection = new DesignCollection();
var here = this;
$('#insert-dynamic-element').click(function(){
var element = new ElementView();
here.collection.addElement(element);
});
},
render: function(){
}
})
return DesignView;
});
If you do not want to move the method from the current model. Then you might have to call a specific model using the index
here.collection.at(0).addElement(element);
But there might be a case when there are no model in the collection and this might lead to a error condition..
here.collection.at(0) && here.collection.at(0).addElement(element);
Well, you need to get a specific model, not the array of them. This seems like an error since you'll be picking a specific model essentially arbitrarily (unless you application has semantics that support this), but this would work:
here.collection.at(0).addElement(element);

backbone.js each function not receiving the models

I am trying to receive a json data and append to element. all are work fine up to i use the static assignments. while i start to fetch the data from server side, or using fetch nothing work for me.. something wrong with my fech process, any can help me to correct my fetch process and update my code.(instead of simply placing the correct code)..
my JSON(sample):
nameing = [
{name:'student4'},
{name:'student5'},
{name:'student6'}
]
Backbone code:
(function($){
var list = {};
list.model = Backbone.Model.extend({
defaults:{
name:'need the name'
}
});
list.collect = Backbone.Collection.extend({
model:list.model,
url : 'data/names.json', //this is correct path.
initialize:function(){
this.fetch();
}
});
list.view = Backbone.View.extend({
initialize:function(){
this.collection = new list.collect();
this.collection.on("reset", this.render, this);
},
render:function(){
_.each(this.collection.models, function(data){
console.log(data); // i am not get any model here... any one correct my code?
})
}
});
var newView = new list.view();
})(jQuery)
thanks in advance.
Your JSON is not valid. Wiki
[
{"name":"student4"},
{"name":"student5"},
{"name":"student6"}
]

Can't display data using Handlebars template and BackboneJS

I have been trying to display some data(a json object with only three properties) by fetching it from server (2 lines of php code). To fetch and display that data in html page I've used BackboneJS and Handlebars template respectively. Here is the javascript code
var User = Backbone.Model.extend({
urlRoot:"getUser/"
});
var UserView = Backbone.View.extend({
el:$("#user"),
initialize: function(){
this.model.bind("change", this.render());
},
render: function(){
var templateSource = $("#user-temp").html();
var template = Handlebars.compile(templateSource);
$(this.el).html(template(this.model));
var newDate = new Date();
console.log("in UserView render :: " + newDate.getTime());
console.log(this.model.toJSON());
//var pp = "nothing";
}
});
var UserRouter = Backbone.Router.extend({
routes:{
"":"userDetails"
},
userDetails:function(){
//var newUser = new User({id:1});
var newUser = new User();
var userView = new UserView({model:newUser});
var newDate = new Date();
newUser.fetch({
success:function(){
console.log("in router :: " + newDate.getTime());
console.log(userView.model.toJSON());
}
});
}
});
Handlebars template in index.html page
<div id="user"></div>
<script id="user-temp" type="text/x-handlebars-template">
<div>
ID {{attributes.id}}
Name {{attributes.name}}
Age {{attributes.age}}
</div>
</script>
PHP code
$user = array("name"=>"Arif","id"=>"1","age"=>"100");
echo json_encode($user);
Now the problem is I can't see the data ($user) i'm sending from server in index.html page, in console (google chrome) i've rather found this
in UserView render() :: 1350880026092
Object
__proto__: Object
in router :: 1350880026097
Object
age: "100"
id: "1"
name: "Arif"
__proto__: Object
(The BIG numbers in console is time in milliseconds.)
But If I change the code for console output (just showing the model)
(in UserView render() function)
console.log(this.model);
(in UserRouter userDetails() function)
console.log(userView.model);
Then the console looks like this
in UserView render :: 1350881027988
child
_changing: false
_escapedAttributes: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object <<======
age: "100"
id: "1"
name: "Arif"
__proto__: Object
changed: Object
cid: "c0"
id: "1"
__proto__: ctor
in router :: 1350881027995
child
_changing: false
_escapedAttributes: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object <<======
age: "100"
id: "1"
name: "Arif"
__proto__: Object
changed: Object
cid: "c0"
id: "1"
__proto__: ctor
Here i can see the attributes (arrow marks <<====== )
So what am i doing wrong? Am i missing some basic concepts here? By the way, I'm new to Handlebars and BackboneJS. Moreover its my first question in stackoverflow, so if you think the info i've given isn't enough, please feel free to ask what further info you need.
Thanks in advance.
You bind your model to this.render() which you means you execute your render function and then bind your model to whatever render returns (nothing, in your case).
Try
initialize: function(){
_.bindAll(this, 'render'); // guarantees the context for render
this.model.bind("change", this.render);
}
or, with a more up to date syntax (see the changelog for 0.9.0 http://backbonejs.org/#changelog, bind and unbind have been renamed to on and off for clarity)
initialize: function(){
_.bindAll(this, 'render');
this.model.on("change", this.render);
}

When is the right time to fetch a collection in Backbone js?

So I'm working on a backbone app, and trying to modularize things as much as I can using require.js. This has been my guide.
I'm having trouble getting my view to always fetch my collection. If I access my app from the base url (myapp.com/), and go to the route of my view, the collection is fetched. If I do not go to the view, and instead access it from myapp.com/#/campaigns, then the collection is not fetched.
Here is some relevant code.
router.js
define([
'jQuery',
'Underscore',
'Backbone',
'views/home/main',
'views/campaigns/list'
], function($, _, Backbone, mainHomeView, campaignListView ){
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'campaigns': 'showCampaigns',
// Default
'*actions': 'defaultAction'
},
showCampaigns: function(){
campaignListView.render();
},
defaultAction: function(actions){
// We have no matching route, lets display the home page
//mainHomeView.render();
}
});
var initialize = function(){
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});
collections/campaigns.js
define([
'jQuery',
'Underscore',
'Backbone',
'models/campaigns'
], function($, _, Backbone, campaignsModel){
var campaignsCollection = Backbone.Collection.extend({
model: campaignsModel,
url: '/campaigns',
initialize: function(){
}
});
return new campaignsCollection;
});
views/campaigns/list.js
define([
'jQuery',
'Underscore',
'Backbone',
'collections/campaigns'
], function($, _, Backbone, campaignsCollection){
var campaignListView = Backbone.View.extend({
el:$('#container'),
initialize:function(){
this.collection = campaignsCollection;
this.collection.fetch();
},
render: function(){
var data = {
campaigns: this.collection,
_: _
};
$('#container').html('Campaigns length: '+data.campaigns.models.length);
}
});
return new campaignListView;
});
Any ideas on what I'm doing wrong? I believe it has something to do with calling this.collection.fetch() in the initalize function of the view. If that is the issue, where should I put fetch()?
The problem in your code is that your campaignListView fetch the collection when it is initialized and it is initialized only once. Your render method which is actually called from the router doesn't call your initialize method of campaignListView, when you change theurl your second time.
You have two options here :
1. return the class not the instance of your campaignListView and initialize it in the router :
// in your router code
showCampaigns: function(){
var campaignListViewInstance = new campaignListView(); // don't forget the brackets after your initialize function
campaignListViewInstance.render();
},
// in your campaignListView code :
return campaignListView; // without "new"
This will initialize the code everytime the router is hit.
2. place your fetch in the render method
// in your campaignListView code :
initialize:function(){
this.collection = campaignsCollection;
},
render: function(){
this.collection.fetch({success: function(collection) {
var data = {
campaigns: collection,
_: _
};
$('#container').html('Campaigns length: '+data.campaigns.models.length);
}); // this will be fetched everytime you render and also it has success callback
}
Also be aware that you should replace all your instance creating functions with brackets at the end
return new campaignListView; --> return new campaignListView();
return new campaignsCollection; --> return new campaignsCollection();
Another problem you will face is the async work of fetch. You should use success or event driven rendering of your collection, because fetch works in the background and you will have no data when you immediately call render after calling fetch.
+1 for your AMD approach.
I should really update the tutorial but it's better to not return instantiated modules. Maybe try checking out http://backboneboilerplate.com

Resources