Hi i am working on a paly2.0 framework application(with java) using backbone.js. In my application i need to get the table data from the database regularly ( for the use case of displaying the upcoming events list and if the crossed the old event should be removed from the list ).I am getting the data to display ,but the issue is to hit the Database regularly.For that i tried use backbone.js polling concept as per these links Polling a Collection with Backbone.js , http://kilon.org/blog/2012/02/backbone-poller/ .But they not polling the latest collection from db. Kindly suggest me how to achieve that or any other alternatives ?
Thanks in adv.
There is not a native way to do it with Backbone. But you could implement long polling requests adding some methods to your collection:
// MyCollection
var MyCollection = Backbone.Collection.extend({
urlRoot: 'backendUrl',
longPolling : false,
intervalMinutes : 2,
initialize : function(){
_.bindAll(this);
},
startLongPolling : function(intervalMinutes){
this.longPolling = true;
if( intervalMinutes ){
this.intervalMinutes = intervalMinutes;
}
this.executeLongPolling();
},
stopLongPolling : function(){
this.longPolling = false;
},
executeLongPolling : function(){
this.fetch({success : this.onFetch});
},
onFetch : function () {
if( this.longPolling ){
setTimeout(this.executeLongPolling, 1000 * 60 * this.intervalMinutes); // in order to update the view each N minutes
}
}
});
var collection = new MyCollection();
collection.startLongPolling();
collection.on('reset', function(){ console.log('Collection fetched'); });
Related
I have been running into the problem of stale collection references. So, I have the following model:
ProcessModel = Backbone.Model.extend({
initialize: function() {
this.set('steps', new StepsCollection());
}
...
});
When the ProcessModel is fetched from the server, the StepsCollection is returned as well. Previously, I had the following parse method:
parse: function(response) {
...
response.steps = new StepsCollection(response.steps, {parse: true});
}
...however, this was creating a brand new collection object, rather than reusing the existing one. This was causing a view which was bound to the previous "steps" collection to become stale.
I've tried the following:
response.steps = this.get('steps').reset(response.steps);
But I get a long stacktrace in Object.Marionette.bindEntityEvents. What am I doing wrong?
Try this. This will create single collection and then we'll reset same collection instance with new data-set inside parse method.
ProcessModel = Backbone.Model.extend({
initialize: function() {
this.myCollection = new StepsCollection();
this.set('steps', this.myCollection);
...
},
parse: function(response) {
this.myCollection.reset(response.steps);
this.set('steps', this.myCollection);
...
}
});
I'm new with backbone and faced the following problems. I'm trying to emulate some sort of "has many relation". To achieve this I'm adding following code to initialize method in the model:
defaults: {
name: '',
tags: []
},
initialize: function() {
var tags = new TagsCollection(this.get('tags'));
tags.url = this.url() + "/tags";
return this.set('tags', tags, {
silent: true
});
}
This code works great if I fetch models through collection. As I understand, first collection gets the data and after that this collection populates models with this data. But when I try to load single model I get my property being overridden with plain Javascript array.
m = new ExampleModel({id: 15})
m.fetch() // property tags get overridden after load
and response:
{
name: 'test',
tags: [
{name: 'tag1'},
{name: 'tag2'}
]
}
Anyone know how to fix this?
One more question. Is there a way to check if model is loaded or not. Yes, I know that we can add callback to the fetch method, but what about something like this model.isLoaded or model.isPending?
Thanks!
"when I try to load single model I get my property being overridden with plain Javascript array"
You can override the Model#parse method to keep your collection getting overwritten:
parse: function(attrs) {
//reset the collection property with the new
//tags you received from the server
var collection = this.get('tags');
collection.reset(attrs.tags);
//replace the raw array with the collection
attrs.tags = collection;
return attrs;
}
"Is there a way to check if model is loaded or not?"
You could compare the model to its defaults. If the model is at its default state (save for its id), it's not loaded. If it doesn't, it's loaded:
isLoaded: function() {
var defaults = _.result(this, 'defaults');
var current = _.wíthout(this.toJSON(), 'id');
//you need to convert the tags to an array so its is comparable
//with the default array. This could also be done by overriding
//Model#toJSON
current.tags = current.tags.toJSON();
return _.isEqual(current, defaults);
}
Alternatively you can hook into the request, sync and error events to keep track of the model syncing state:
initialize: function() {
var self = this;
//pending when a request is started
this.on('request', function() {
self.isPending = true;
self.isLoaded = false;
});
//loaded when a request finishes
this.on('sync', function() {
self.isPending = false;
self.isLoaded = true;
});
//neither pending nor loaded when a request errors
this.on('error', function() {
self.isPending = false;
self.isLoaded = false;
});
}
I have a problem while initializing a Backbone model with some data coming from Jackson.
The received data happens to have a listPropertyValue, which is originally a Java List of objects. When doing the initialize() method I make it a Backbone collection without much problem.
But the final SomeModel constructor also adds an attribute called listPropertyValue as a JavaScript array, which I don't want.
How may I discard or reject this array and which is the right way to do it?
Here is my code:
var SomeModel = Backbone.Model.extend({
defaults : {
id:null,
name:'',
order:null,
isRequired:null,
}
initialize : function(options) {
if(options.listPropertyValue !== undefined) {
this.set('collectionPropertyValue', new PropertyValueCollection(options.listPropertyValue))
}
// I thought of doing this. Don't know if it's the right thing to do
// this.unset('listPropertyValue', { silent: true });
}
My concern is not only how to do it, but how to do it in a proper Backbone way.
(I assume you're getting this data from an API somewhere.)
You should define a parse method in your model to return only the data you're interested in:
parse: function(response){
return _.omit(response, "listPropertyValue");
}
Backbone will do the rest for you: every time it receives API from the data it will call parse automatically.
For more info: http://backbonejs.org/#Model-parse
I finally did it. I used the same code I published but it didn't work until I used backbone with version 1.1.2 (I was using 1.0.0 or similar).
var SomeModel = Backbone.Model.extend({
defaults : {
id:null,
name:'',
order:null,
isRequired:null,
}
initialize : function(options) {
if(options.listPropertyValue !== undefined) {
this.set('collectionPropertyValue', new PropertyValueCollection(options.listPropertyValue));
}
this.unset('listPropertyValue', {
silent : true
});
}
}
This problem just seemed to appear while I updated to Backbone 1.1. I have a nested Backbone model:
var ProblemSet = Backbone.Model.extend({
defaults: {
name: "",
open_date: "",
due_date: ""},
parse: function (response) {
response.name = response.set_id;
response.problems = new ProblemList(response.problems);
return response;
}
});
var ProblemList = Backbone.Collection.extend({
model: Problem
});
I initially load in a ProblemSetList, which is a collection of ProblemSet models in my page. Any changes to the open_date or due_date fields of any ProblemSet, first go to the server and update that property, then returns. This fires another change event on the ProblemSet.
It appears that all subsequent returns from the server fires another change event and the changed attribute is the "problems" attribute. This results in infinite recursive calls.
The problem appears to come from the part of set method of Backbone.Model (code listed here from line 339)
// For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
if (!_.isEqual(current[attr], val)) changes.push(attr);
if (!_.isEqual(prev[attr], val)) {
this.changed[attr] = val;
} else {
delete this.changed[attr];
}
unset ? delete current[attr] : current[attr] = val;
}
// Trigger all relevant attribute changes.
if (!silent) {
if (changes.length) this._pending = true;
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
The comparison on the problems attribute returns false from _.isEqual() and therefore fires a change event.
My question is: is this the right way to do a nested Backbone model? I had something similar working in Backbone 1.1. Other thoughts about how to proceed to avoid this issue?
You reinstantiate your problems attribute each time your model.fetch completes, the objects are different and thus trigger a new cycle.
What I usually do to handle nested models:
use a model property outside of the attributes handled by Backbone,
instantiate it in the initialize function,
set or reset this object in the parent parse function and return a response omitting the set data
Something like this:
var ProblemSet = Backbone.Model.extend({
defaults: {
name: "",
open_date: "",
due_date: ""
},
initialize: function (opts) {
var pbs = (opts && opts.problems) ? opts.problems : [];
this.problems = new ProblemList(pbs);
},
parse: function (response) {
response.name = response.set_id;
if (response.problems)
this.problems.set(response.problems);
return _.omit(response, 'problems');
}
});
parse gets called on fetch and save (according to backbone documentation), this might cause your infinite loop. I don't think that the parse function is the right place to create the new ProblemsList sub-collection, do it in the initialize function of your model instead.
Having some issues with pulling calendar events from Google Calendar using Backbone.
When I call collection.fetch() I am only getting a length of 1 returned, when there are 13 objects in the json.
I had a look at the parse:function(response) method that I am overriding in the Collection, and it is returning all 13 objects. I had a look at the add method in backbone.js, and the issue appears to occur on line 591:
models = _.isArray(models) ? models.slice() : [models];
When I wrap the line with console.log to check the status of the models variable:
console.log(models);
models = _.isArray(models) ? models.slice() : [models];
console.log(models);
I get the following result:
[Object,Object,Object,Object,Object,Object,Object,Object,Object,Object,Object,Object,Object] backbone.js:590
[child,undefined × 12]
I'm at a loss to explain why it would be failing on add. I have checked each model by changing the parse:function(response) method in the collection to return each object, and it works fine.:
parse: function(response) {
return response.feed.entry[5];
}
I have successfully parsed Google Calendar feeds with Backbone.js before, so I fear I am missing something really simple.
If I console.log response.feed the following is returned:
This is the full class:
/**
* Backbone
* #class
*/
var Gigs = Gigs || {};
Gigs.Backbone = {}
Gigs.Backbone.Model = Backbone.Model.extend();
Gigs.Backbone.Collection = Backbone.Collection.extend({
model: Gigs.Backbone.Model,
url: 'http://www.google.com/calendar/feeds/email#email.com/public/full?alt=json-in-script&orderby=starttime&callback=?',
sync: function(method, model, options) {
options.dataType = "jsonp";
return Backbone.sync(method, model, options);
},
parse: function(response) {
return response.feed.entry;
}
});
Gigs.Backbone.Controller = Backbone.View.extend({
initialize: function() {
var self = this;
this.collection = new Gigs.Backbone.Collection();
this.collection.on('reset', this.addElements, this);
this.collection.fetch();
},
addElements: function() {
log(this.collection);
}
});
var backbone = new Gigs.Backbone.Controller();
Apparently, Google Calendar provides its entries with an id wrapped in an object 1:
"id":{
"$t":"http://www.google.com/calendar/feeds/..."
}
which Backbone seems to dislike. A lot.
One simple solution would be to overwrite the id in your parse method:
parse: function(response) {
var entries=[];
_.each(response.feed.entry, function(entry,ix) {
entry.id=entry.id.$t;
entries.push(entry);
});
return entries;
}
And a Fiddle http://jsfiddle.net/bqzkT/
1 Check https://developers.google.com/gdata/docs/json to see how Google converts its XML data to JSON.
Edit : the problem comes from the way the data is returned with a straight XML to JSON conversion (requested via the alt=json-in-script parameter) wrapping the attributes in objects. Changing this parameter to alt=jsonc yields a much simpler JSON representation. Compare a jsonc output to the json-in-script equivalent.
See https://developers.google.com/youtube/2.0/developers_guide_jsonc#Comparing_JSON_and_JSONC for more info