Backbone.js Collection fetch success return - backbone.js

I am trying to get return from Backbone.js Collection fetch using code similar to one below. How do I bypass rest ?
var myData = getCollection();
getCollection(){
this.collection = fetchCollection.fetch({
success: function(collection, data) {
collectionData = fetchCollection.toJSON();
return collectionData;
}
},this);
}

fetch calls the underlying sync methods, Backbone.sync. You can either override the sync method of individual the collections or models, or replace Backbone.sync with your own. One such alternative sync implementation for instance is the Backbone localStorage adaptor which stores to localStorage instead of using REST to talk to a server.

Related

Create a model on the server with an Id that was set on the client

Is there a way to create a model with POST request event if the id for the model is set in browser. So normally backbone will make a PUT request when the id still exists in the model, and I cant find a way to work around this behavior.
Override the sync method in the model.
sync: function(method, model, options) {
if (method === 'update') {
method = 'create';
}
return Backbone.sync.call(this, method, model, options);
}
Have you tried turning on emulateHTTP.

Using a Backbone Collection without a data source (no url)

First time posting here... looking forward to see how it all works, but have really appreciated reading other's questions & answers.
I am using backbone for a small app and have found it helpful to use a collection to store some information that is only required during the current session. (I have a number of collections and all the others connect to my API to store/retrieve data).
I read here (in backbone.js can a Model be without any url?) that it is possible, and even good to use a collection without providing a url.
Now I would like to add a row of data to the collection... simple:
myCollection.create(data);
but of course that now throws an error:
Uncaught Error: A "url" property or function must be specified
Is there any way to use a Backbone collection, be able to add new rows of data (models) to it, but not sync to any sort of data source. Or can you suggest another solution.
I guess I could just use an object to hold the data, but I was enjoying the consistency and functionality.
I am using Backbone.Marionette if that has any impact.
Thanks in advance.
One thing you could do is override the Backbone.Model methods that communicate with the server, i.e. sync, fetch, and save... for example:
var App = {
Models: {},
Collections: {}
};
App.Models.NoUrlModel = Backbone.Model.extend({});
App.Models.NoUrlModel.prototype.sync = function() { return null; };
App.Models.NoUrlModel.prototype.fetch = function() { return null; };
App.Models.NoUrlModel.prototype.save = function() { return null; };
App.Collections.NoUrlModels = Backbone.Collection.extend({
model: App.Models.NoUrlModel,
initialize: function(){}
});
var noUrlModels = new App.Collections.NoUrlModels();
noUrlModels.create({'foo': 'bar'}); // no error
// noUrlModels.models[0].attributes == {'foo': 'bar'};
See Demo

how backbone.js model fetch method works

i am very confuse about using backbone.js model fetch method. See the following example
backbone router:
profile: function(id) {
var model = new Account({id:id});
console.log("<---------profile router-------->");
this.changeView(new ProfileView({model:model}));
model.fetch();
}
the first step, the model account will be instantiated, the account model looks like this.
define(['models/StatusCollection'], function(StatusCollection) {
var Account = Backbone.Model.extend({
urlRoot: '/accounts',
initialize: function() {
this.status = new StatusCollection();
this.status.url = '/accounts/' + this.id + '/status';
this.activity = new StatusCollection();
this.activity.url = '/accounts/' + this.id + '/activity';
}
});
return Account;
});
urlRoot property for what is it? After model object created, the profileview will be rendered with this this.changeView(new ProfileView({model:model}));, the changeview function looks like this.
changeView: function(view) {
if ( null != this.currentView ) {
this.currentView.undelegateEvents();
}
this.currentView = view;
this.currentView.render();
},
after render view, profile information will not display yet, but after model.fetch(); statement execute, data from model will be displayed, why? I really don't know how fetch works, i try to find out, but no chance.
I'm not entirely sure what your question is here, but I will do my best to explain what I can.
The concept behind the urlRoot is that would be the base URL and child elements would be fetched below it with the id added to that urlRoot.
For example, the following code:
var Account = Backbone.Model.extend({
urlRoot: '/accounts'
});
will set the base url. Then if you were to instantiate this and call fetch():
var anAccount = new Account({id: 'abcd1234'});
anAccount.fetch();
it would make the following request:
GET /accounts/abcd1234
In your case there, you are setting the urlRoot and then explicitly setting a url so the urlRoot you provided would be ignored.
I encourage you to look into the Backbone source (it's surprisingly succinct) to see how the url is derived: http://backbonejs.org/docs/backbone.html#section-65
To answer your other question, the reason your profile information will not display immediately is that fetch() goes out to the network, goes to your server, and has to wait for a reply before it can be displayed.
This is not instant.
It is done in a non-blocking fashion, meaning it will make the request, continue on doing what it's doing, and when the request comes back from the server, it fires an event which Backbone uses to make sure anything else that had to be done, now that you have the model's data, is done.
I've put some comments in your snippet to explain what's going on here:
profile: function(id) {
// You are instantiating a model, giving it the id passed to it as an argument
var model = new Account({id:id});
console.log("<---------profile router-------->");
// You are instantiating a new view with a fresh model, but its data has
// not yet been fetched so the view will not display properly
this.changeView(new ProfileView({model:model}));
// You are fetching the data here. It will be a little while while the request goes
// from your browser, over the network, hits the server, gets the response. After
// getting the response, this will fire a 'sync' event which your view can use to
// re-render now that your model has its data.
model.fetch();
}
So if you want to ensure your view is updated after the model has been fetched there are a few ways you can do that: (1) pass a success callback to model.fetch() (2) register a handler on your view watches for the 'sync' event, re-renders the view when it returns (3) put the code for instantiating your view in a success callback, that way the view won't be created until after the network request returns and your model has its data.

How to fetch and populate backbone model for Google Places JS API?

I'm implementing a system that require access to Google Places JS API. I've been using rails for most of the project, but now I want to inject a bit of AJAX in one of my views. Basically it is a view that displays places near your location. For this, I'm using the JS API of Google places. A quick workflow would be:
1- The user inputs a text query and hits enter.
2- There is an AJAX call to request data from Google Places API.
3- The successful result is presented to the user.
The problem is primarily in step 2. I want to use backbone for this but when I create a backbone model, it requests to the 'rootURL'. This wouldn't be a problem if the requests to Places was done from the server but it is not.
A place call is done like this:
service = new google.maps.places.PlacesService(map);
service.nearbySearch(request, callback);
Passing a callback function:
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
createMarker(results[i]);
}
}
}
Is it possible to override the 'fetch' method in backbone model and populate the model with the successful Places result? Is this a bad idea?
It is possible to override the fetch method of your backbone model.
var mapModel = Backbone.Model.extend({
fetch: function (options) {
// do your call to google places here
},
callBackFunctionForGoogleMaps: function (results, status) {
// call back function here would set model properties
}
});
return mapModel;
This way you override fetch and remove the defaults behavior of Backbone to make an ajax call.
Just as an FYI if you want to override Backbone models fetch but still have the default behavior of model.fetch you can do the following. Note the return calling Backbone.Model.fetch.
var mapModel = Backbone.Model.extend({
fetch: function (options) {
// do any pre-fetch actions here
return Backbone.Model.fetch.call(options);
}
});
return mapModel;
It is probably not a bad idea to override the fetch method here because you are still fetching data for your model, just not through ajax calls on your end. It would be smart though to leave comments noting that you are overriding fetch in this manner for a reason.

Wait for the collection to fetch everything in backbone

I have two set of collections. One is for the categories and the other is for the Items. I ned to wait for the categories to finish fetching everything for me to set the category for the Items to be fetched.
Also i everytime i click a category i must re-fetch a new Items Collection because i have a pagination going on everytime i click on a category it doesn't refresh or re-fetch the collection so the pagination code is messing with the wrong collection. Any ideas?
this.categoryCollection = new CategoryCollection();
this.categoryCollection.fetch();
this.itemCollection = new ItemCollection();
this.itemCollection.fetch();
Just ran into a similar situation. I ended up passing jquery.ajax parameters to the fetch() call. You can make the first fetch synchronous. From the backbone docs:
jQuery.ajax options can also be passed directly as fetch options
Your code could be simplified to something like:
this.categoryCollection.fetch({async:false});
this.itemCollection.fetch();
One quick way would be to just pass a callback into the first fetch() call that invokes the second. fetch() takes an options object that supports a success (and error) callback.
var self = this;
this.categoryCollection = new CategoryCollection();
this.categoryCollection.fetch({
success: function () {
self.itemCollection = new ItemCollection();
self.itemCollection.fetch();
}
});
Not the most elegant, but it works. You could probably do some creative stuff with deferreds since fetch() returns the jQuery deferred that gets created by the $.ajax call that happens.
For the pagination issue, it's difficult to tell without seeing what your pagination code is doing. You're going to have to roll the pagination stuff yourself since Backbone doesn't support it natively. What I'd probably do is create a new Collection for the page criteria that are being queried and probably create a server action I could hit that would support the pagination (mapping the Collection's url to the paginated server action). I haven't put a ton of thought into that, though.
I had to react to this thread because of the answers there.
This is ONLY WAY OF DOING THIS RIGHT!!!
this.categoryCollection = new CategoryCollection();
this.itemCollection = new ItemCollection();
var d1 = this.categoryCollection.fetch();
var d2 = this.itemCollection.fetch();
jQuery.when(d1, d2).done(function () {
// moment when both collections are populated
alert('YOUR COLLECTIONS ARE LOADED :)');
});
By doing that you are fetching both collections at same time and you can have event when both are ready. So you don't wait to finish loading first collections in order to fetch other, you are not making ajax calls sync etc that you can see in other answers!
Here is a doc on Deferred objects.
Note: in this example case when one or more deferred object fails it's not covered. If you want to cover that case also beside .done you will have to add .fail callback on .when and also add error handler that will mark failed d1 or d2 in this example.
I am using RelationalModel and I created a queued fetch, that only calls the 'change' event when done loading:
var MySuperClass = Backbone.RelationalModel.extend({
//...
_fetchQueue : [],
fetchQueueingChange : function(name){
//Add property to the queue
this._fetchQueue.push(name);
var me = this;
//On fetch finished, remove it
var oncomplete = function(model, response){
//From _fetchQueue remove 'name'
var i = me._fetchQueue.indexOf(name);
me._fetchQueue.splice(i, 1);
//If done, trigger change
if (me._fetchQueue.length == 0){
me.trigger('change');
}
};
this.get(name).fetch({
success: oncomplete,
error : oncomplete
});
},
//...
});
The class would call:
this.fetchQueueingChange('categories');
this.fetchQueueingChange('items');
I hope you can improve on this, it worked well for me.
I ended up with the same problem today and figured out a solution to this:
var self = this;
this.collection = new WineCollection();
this.collection.url = ApiConfig.winetards.getWineList;
this.collection.on("reset", function(){self.render()});
this.collection.fetch({reset: true});
Now when the fetch on the collection is complete a "reset" is triggered and upon "reset" call the render() method for the view.
Using {async: false} is not the ideal way to deal with Backbone's fetch().
just set jQuery to become synchronous
$.ajaxSetup({
async: false
});
this.categoryCollection.fetch();
this.itemCollection.fetch();
$.ajaxSetup({
async: true
});
This is the simplest solution, I guess. Of course, starting new requests while these fetches run will be started as synchronous too, which might be something you don't like.

Resources