I have a Backbone Collection that represent a json objects and I'm trying to render this objects in the View that would iterate them into template. I think the breakpoint was that i can't read the json array into array
The collection return array
{
"calls": [
{
"callId": "173",
"company": "Company 1",
"status": "open",
"DueDate": "2013-06-10 00:00:00",
"EmployeeId": "12"
},
{
"callId": "170",
"company": "company 2",
"status": "done",
"DueDate": "2013-05-27 14:27:37",
"EmployeeId": "11"
},
{
"callId": "169",
"company": "Company 3",
"status": "open",
"DueDate": "2013-05-20 00:00:00",
"EmployeeId": "11"
}
]
}
Route
// Filename: router.js
define([
'jquery',
'underscore',
'backbone',
'app/collections/schedule',
'app/views/schedule',
'app/views/dashboard'
], function($, _, Backbone, ScheduleCollection, ScheduleView, DashboardView) {
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'dash': 'defaultRoute',
'schedule': 'scheduleRoute',
'accounts': 'accountsRoute',
'reports': 'reportsRoute',
// Default
'*actions': 'defaultRoute'
},
scheduleRoute: function() {
// Create a new instance of the collection
// You need to set the url in the collection as well to hit the server
var schedulecollection = new ScheduleCollection();
// Pass in the collection as the view expects it
var scheduleview = new ScheduleView({
collection: schedulecollection
});
//scheduleview.initialize();
// No need of calling render here
// as the render is hooked up to the reset event on collection
},
defaultRoute: function(actions) {
// We have no matching route, lets display the home page
DashboardView.render();
}
});
var initialize = function() {
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});
Collection
// Filename: collections/schedule
define([
'jquery',
'underscore',
'backbone',
'app/models/schedule'
], function ($, _, Backbone, ScheduleModel) {
var ScheduleCollection = Backbone.Collection.extend({
model: ScheduleModel,
url: "http://sam-client:8888/sam-client/i/schedule",
initialize: function () {
//console.log('schedule collections loaded successfully');
}
});
return ScheduleCollection;
});
View
// Filename: views/schedule
define([
'jquery',
'underscore',
'backbone',
'text!templates/schedule.html'
], function ($, _, Backbone, ScheduleTemplate) {
var scheduleView = Backbone.View.extend({
el: $("#test"),
initialize: function () {
// Listen to the reset event which would call render
this.listenTo(this.collection, 'reset', this.render());
// Fetch the collection that will populate the collection
// with the response
this.collection.fetch();
},
render: function () {
var data = {
schedule: this.collection.models,
_: _
};
var compiledTemplate = _.template(ScheduleTemplate, data);
this.$el.html(compiledTemplate);
}
});
return scheduleView;
});
in The template
<ul>
<% _.each(schedule, function(call){ %>
<li><%= call.get("company") %> - <%= call.get("DueDate") %></li>
<% }); %>
</ul>
The problem is there is no data passed in the template in order to iterate if it
The behavior of Collection#fetch changed in Backbone 1.0:
fetch collection.fetch([options])
[...] When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, in which case the collection will be (efficiently) reset.
So just this:
this.collection.fetch()
will trigger 'add', 'remove', and 'change' events in Backbone 1.0+ rather than a single 'reset' event like older versions of Backbone. If you say:
this.collection.fetch({ reset: true })
then you'll get your 'reset' event.
The next problem is your JSON format. Backbone collections expect the incoming data to be a simple array of model attribute objects but your JSON has the array nested inside "calls". You can provide a parse method in your collection to unwrap things:
parse: function(response) {
return response.calls;
}
You'd want that method inside your ScheduleCollection.
Related
I'm trying to figure it out how to get a model by it's id and display the same to view. I have setup my router to get the data by id. I tried a lot but unable to find a solution. Any idea on this would be appreciated.
collection.js file:
app.Collections.qualityCollection = Backbone.Collection.extend({
model: app.Models.qualityModel,
url: "http://localhost/socialapp/php/fetch.php"
});
var userCollections = new app.Collections.qualityCollection();
userCollections.fetch();
router.js file:
app.Routers.socialRouter = Backbone.Router.extend({
routes: {
"" : "homePage",
"make_friends/:id" : "userProfile",
},
userProfile: function() {
new app.Views.idView({ model: userCollections.get(id) });
}
});
new app.Routers.socialRouter();
Backbone.history.start();
view.js file:
app.Views.idView = Backbone.View.extend({
el: $("#app"),
template: _.template($('#public-profile').html()),
initialize: function(){
this.render();
},
render: function () {
console.log(this.model); /// undefined
this.$el.html(this.template( this.model.toJSON() ));
return this;
}
});
JSON Data from the URL:
[
{
"id": "1",
"firstname": "Abc",
"lastname": "Xyz"
},
{
"id": "2",
"firstname": "Klm",
"lastname": "Opq"
}
]
Expected Router Link: http://localhost/socialapp/#/make_friends/2
To answer your question, you receive the value of route param in the callback. You can just pass it to the view and access it in it's initialize
app.Routers.socialRouter = Backbone.Router.extend({
routes: {
"" : "homePage",
"make_friends/:id" : "userProfile",
},
userProfile: function(id) {
// no reference?
new app.Views.idView({ collection: userCollections, modelId: id });
}
});
If you're view is actually an item view, you don't need to pass the entire collection, you can just do the following after fetching the collection in router.
new app.Views.idView({ model: userCollections.get(id) });
Better yet, your view don't need a collection, you just need a model instance with an end point that returns a model's info
userCollections.get(id) should work.
Considering Id is attribute of the model, you can find the model as below too.
userCollections.find(function(model) { return model.get('id') === Id; });
I am trying to view the collection i import from the service, using backbone on the client side, and python/flask as the service. When i make the GET request, i get the following data back:
{"entries": [{"Title": "my title 1", "Description": "My desc 1", "Info": "Info 1", "ids": 1}, {"Title": "my title 2", "Description": "My desc 2", "Info": "Info 2", "ids": 2}]}
But those entries are not showing on my page, even though i use fetch. This is my ListView code:
var app = app || {};
app.ContactListView = Backbone.View.extend({
el: '#contacts',
initialize: function () {
this.collection = new app.ContactList();
this.collection.fetch({reset: true});
this.render();
this.listenTo( this.collection, 'reset', this.render );
},
render: function () {
this.collection.each(function( item ){
this.renderContact( item );
}, this );
},
renderContact: function ( item ) {
var contactView = new app.ContactView({
model: item
});
this.$('#ContactTable').append( contactView.render().el );
}
});
What might be the cause?
The reason is because your collection is expecting an array of models as it's response but your service is returning the array under entries. From the documentation
parse is called by Backbone whenever a collection's models are
returned by the server, in fetch. The function is passed the raw
response object, and should return the array of model attributes to be
added to the collection.
To work around that you can simply override your parse method to return the array of models.
For example:
app.ContactList = Backbone.Collection.extend({
//...
parse: function (response) {
return response.entries;
}
})
Collection
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
console.log("Loaded");
var Jobs = Backbone.Collection.extend({
url: function () {
return 'http://domain.com/api/jobs?page='+this.page+''
},
page: 1
});
return Jobs;
});
Model
define([
'underscore',
'backbone'
], function(_, Backbone){
var JobFilterModel = Backbone.Model.extend({
defaults: {
T: '1',
PT: '1',
C: '1',
I: '1'
}
});
// Return the model for the module
return JobFilterModel;
});
In one of my view , i SET the models
var jobListFilterModelUpdate = new JobListFilterModel();
jobListFilterModelUpdate.set({value:isChecked});
I'm trying to retrieve the MODEL from Collection so i can send the correct QUERY with the URL.
Question 1
How do i retrieve from Model from Collection
Question 2
Will the retrieved collection be the updated Model with the data i Set in View?
When you declare a Collection you need to specify a model property, something like :
var Jobs = Backbone.Collection.extend({
url: function () {
return 'http://punchgag.com/api/jobs?page='+this.page+''
},
page: 1,
model: JobFilterModel
});
After creating a new model you need to add it to collection (assuming you have jobsCollection created):
var jobListFilterModelUpdate = new JobListFilterModel();
jobListFilterModelUpdate.set({value:isChecked});
jobsCollection.add(jobListFilterModelUpdate);
Answer 1
You can retrieve a model from a collection based on id, collection.get(id). Here JobFilterModel doesn't seem to have any id (you can set idAttribute property of Model to create custom id property). Backbone also creates unique ids at client side but I don't know how would they be of any help to you. If you want to retrieve a model based on any of model's property you can use collection.findWhere() or collection.where().
Answer 2
Yes. It will be but it depends on how link your View to Collection.
Please read till the end (i make a reference to console.log at the end)
the model:
// Spot model:
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var Spot = Backbone.Model.extend({
url: function(){
if (this.get('id') !== null){
return '/spot/' + this.get('id');
}
return '/spots';
},
idAttribute: "id",
defaults: {
id: null,
details:{
name: "school",
type: 4,
rank: 3,
comment: null
},
location:{
address: '',
lat: 70.345,
lng: 90.123
},
user:{
id: 12345,
name: "magneto"
}
},
parse: function(response){
/* var attrs = {
details: {},
location: {},
user: {}
};
attrs.id = response.id;
attrs.details.name = response.details.name;
attrs.details.type = response.details.type;
attrs.details.rank = response.details.rank;
attrs.location.lat = response.location.lat;
attrs.location.lng = response.location.lng;
return attrs; */
}
});
return Spot;
});
the collection:
// Spots collection:
define([
'jquery',
'underscore',
'backbone',
'models/Spot'
], function($, _, Backbone,spot_model){
var Spots = Backbone.Collection.extend({
model: spot_model,
url:
function() {
return '/projects/FE/spots';
},
parse: function(response){
/* var parsed = [],
key;
_.each(response, function (value) {
parsed.push(value);
}, this);
return parsed;*/
},
comparator: function(spot){
return spot.rank;
}
});
return Spots;
});
the view:
// inside our view named: SpotsView
define([
'jquery',
'underscore',
'backbone',
'mustache',
'icanhaz',
'views/spots/Spot',
'collections/Spots',
'text!../../../../templates/spots/spots.mustache!strip',
], function($,
_,
Backbone,
mustache,
ich,
SpotView,
Spots,
SpotsTemplate){
var SpotsView = Backbone.View.extend({
//el: $("#container"),
tagName: "ul",
initialize: function(){
console.log('initialize view');
console.log(this.collection);
_.bindAll(this,'render');
},
render: function(){
console.log(this.collection);
console.log('length: ' + this.collection.length);
console.log('models: ' + this.collection.models);
_.each(this.collection.models, function (spot) {
$(this.el).append(new SpotView({model:spot}).render().el);
}, this);
return this;
}
});
return SpotsView;
});
inside our app.js
var spots = new Spots({model: Spot}),
spotsview;
spots.reset();
console.log(spots.fetch());
console.log('spots:');
console.log(spots.length);
console.log(spots);
spotsview = new SpotsView({collection: spots});
Server outputs
// SERVER output(s) i tried:
{"id": 666,"details":{"name": "mark","type":4,"rank":3.1,"comment":"nothing"},"location":{"lat":70.123,"lng":90.321,"address":"5th avenue"},"user":{"id":333,"name":"wolverine"}}
// another one i tried:
[{"id": 666,"details":{"name": "mark","type":4,"rank":3.1,"comment":"nothing"},"location":{"lat":70.123,"lng":90.321,"address":"5th avenue"},"user":{"id":333,"name":"wolverine"}}]
// another one:
[{"id":"55","details":{"name":"Dan","type":"6","rank":"-9.9","comment":"myEx"},"location":{"lat":"40.780346","lng":"-73.957657","address":"78, West 86th Stree"}},{"id":"57","details":{"name":"Ron","type":"6","rank":"3.0","comment":"Butch"},"location":{"lat":"40.715569","lng":"-73.832428","address":"1344 Queens Boulevard"}},{"id":"58","details":{"name":"Monk's","type":"11","rank":"9.5","comment":"yesss"},"location":{"lat":"40.805443","lng":"-73.965561","address":"112th and broadway "}}]
// without using "parse" method in the collection and in the model (they are commented) i get:
d
_byCid: Object
_byId: Object
length: 0
models: Array[0]
__proto__: x
// when not commented (parse in both collection and model) i get:
_byCid: Object
_byId: Object
length: 6
models: Array[6]
__proto__: x
// despite it says 6, in the view you can see there are lines:
//console.log(this.collection); <--- returns the abovee
//console.log('length: ' + this.collection.length); <-- returns 0
//console.log('models: ' + this.collection.models); <--- none
I also tried removing all properties defenitions from the model. Still didn't work.
The return value content type is: application/json (verified) and is valid json.
I've read:
Backbonejs collection length always zero
but inspite of console.log , showing 0 length , also:
for(var i=0; i< this.collection.length; i++){
console.log(this.collection.get(i));
}
doens't work!
also i've read
Did backbone collection automatically parse the loaded data
Thanks a lot
Update:
I've probably solved it: i've removed even the "parse" declarations from the model and the collection and It worked: length: 6 SpotsView.js models: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object] Either way i would like to know the proper use , if i did right and HOW to use PARSE in both situations the right way (collection and model wise), what do you return in both (!). Tnx
Collection.fetch is an asynchronous operation, therefore when you fetch a collection, the ajax call will be sent and then your code will continue being run just like nothing happened.
spots.reset(); // emptied your collections
console.log(spots.fetch()); // fetch your collection (why is this inside a console.log???)
console.log('spots:'); // log some text, this will be executed right after your fetch call has been made
console.log(spots.length); // here the fetch call probably hasn't returned yet, so returns 0
console.log(spots); // same as above, so returns an empty collection
spotsview = new SpotsView({collection: spots}); // the collection might or might not get populated by the time you get to rendering so there is probably variation from program run to another
So how to fix this? In the view bind the render function to the collection's reset event (launched after each successful fetch or forced reset). This way the view will render only when it has something to show.
// view's initialize method
initialize: function() {
...
this.collection.on('reset', this.render);
...
}
When requesting for data.json file for populating collection which has below data
[{
"Id": "BVwi1",
"Name": "Bag It",
"AverageRating": 4.6,
"ReleaseYear": 2010,
"Url": "http://www.netflix.com/Movie/Bag_It/70153545",
"Rating": "NR"
}, {
"Id": "BW1Ss",
"Name": "Lost Boy: The Next Chapter",
"AverageRating": 4.6,
"ReleaseYear": 2009,
"Url": "http://www.netflix.com/Movie/Lost_Boy_The_Next_Chapter/70171826",
"Rating": "NR"
}]
Collection does not invoke the "Reset" event as the documentation says it should. I can view the request and response are correct after the fetch method but nothing happens. Below is the code for my app.
Router that start's everything
Theater.Router = Backbone.Router.extend({
routes: {
"": "defaultRoute"
},
defaultRoute: function () {
Theater.movies = new Theater.Collections.Movies()
new Theater.Views.Movies({
collection: Theater.movies
});
Theater.movies.fetch();
}
})
var appRouter = new Theater.Router();
Backbone.history.start();
the Collection
Theater.Collections.Movies = Backbone.Collection.extend({
model: Theater.Models.Movie,
url: "scripts/data/data.json",
initialize: function () {}
});
View that subscribes to the reset event
Theater.Views.Movies = Backbone.View.extend({
initialize: function () {
_.bindAll(this, "render", "addOne");
this.collection.bind("reset", this.render);
this.collection.bind("add", this.addOne);
},
render: function(){
console.log("render")
console.log(this.collection.length);
},
addOne: function (model) {
console.log("addOne")
}
})
Reference Site
http://bardevblog.wordpress.com/2012/01/16/understanding-backbone-js-simple-example/
You should tell Backbone to fire the reset on fetch by passing {reset: true} when fetching as of Backbone 1.0
Replace :
Theater.movies.fetch()
With
Theater.movies.fetch({reset :true})
I had a similar issue, I hope my reply will be of any use to others. At first my data.json file was not valid. Then it turned out that I overlooked the following line of code:
Theater.Models.Movie = Backbone.Model.extend({}
Adding this line of code resolved the issue for me.
There might be a problem with your fetch if the collection is not being populated. See this answer to see how to pass an error handler into the fetch operation.
I have the same iusse.. and fixed it by:
youColloection.fetch({reset: true});