getting Backbone collection size? - backbone.js

I've a Backbone Collection
var users = Backbone.Collection.extend({
url: 'https://localhost:2222/users',
model: User
});
In Controller I need to get no of users.
var usersList = new Users();
usersList .fetch({
beforeSend: CommonUtils.sendAuthentication,
data: $.param({ group: group.id})
});
I tried usersList.length it returns 0 but console shows some data in 'usersList'
I logged
console.log(usersList);
console.log(usersList.length);
in 'child' object length shows as 0 but inside that length is 4.

Are you sure you are counting the Length after the fetch has come in? Remember that fetching takes some time and your javascript will not wait for it.

By default Backbone .fetch() makes asynchronous HTTP (Ajax) request which does not finish right away, that is why you have to use success and error callback functions which will be fired accordingly after .fetch() returns a response from the server.
Both success and error receives fetched collection, server response and option objects.
Simple example:
usersList.fetch({
success: function(collection, response, options){
console.log(collection.length);
// Method which would use your fetched colletion
myFancyMethodUsingCollection(collection);
},
error: function(collection, response, options){
}
});
To expand, Backbone collection.fetch([options]) is based on Backbone.sync(method, model, [options]) which is based on jQuery.ajax which by default performs asynchronous requests.
Behavior of .fetch() can be modified by passing options to the method.
Options may contain settings for Backbone (for ex.: silent fetch which does not fire collection changed event and so on.) or jQuery.ajax settings.
Note that using async: false option is discouraged by jQuery.
Synchronous requests may temporarily lock the browser, disabling any
actions while the request is active. As of jQuery 1.8, the use of
async: false with jqXHR ($.Deferred) is deprecated; you must use the
success/error/complete callback options instead.
Backbone.Collection.fetch()
Backbone.sync()
jQuery.ajax()

Related

Don't execute a $resource request in angular when there is already a request running

I use a interval of 10 seconds for sending a request to get the most recent data:
var pollInterval = 10000;
var poll;
poll= $interval(function()
{
getNewestData();//$resource factory to get server data
}, pollInterval );
This works fine voor 99% of the time, but if the internet speed is really slow(I have actually experienced this), It will send the next request before the current is finished. Is there a way to just skip the current interval request if the previous one is still busy? Obsiously I could just use booleans to keep the state of the request, but I wonder if there is a better(native to angular) way of doing this?
Use the $resolved property of the Resource object to check if the previous operation is done.
From the Docs:
The Resource instances and collections have these additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
$resolved: true after first server interaction is completed (either with success or rejection), false before that. Knowing if the Resource has been resolved is useful in data-binding.
$cancelRequest: If there is a cancellable, pending request related to the instance or collection, calling this method will abort the request.
-- AngularJS ngResource $resource API Reference.
How about making the request, then waiting for that to complete and then wait 10 seconds before making the same request again? Something along this line:
var pollInterval = 10000;
var getNewestData = function () {
// returns data in promise using $http, $resource or some other way
};
var getNewestDataContinuously = function () {
getNewestData().then(function (data) {
// do something with the data
$timeout(function () {
getNewestDataContinuously();
}, pollInterval);
});
};
getNewestData is the function that actually makes the request and returns the data in a promise.
And once data is fetched, a $timeout is started with timer as 10 seconds which then repeats the process.

angularjs clearing the cache from a particular request

All the http requests I'm making are cached by default. Now, how do I clear the cache of a particular request?
Here is a situation to explain better. I have a REST API that sends two different kinds of data based on the authentication. Whenever the User has authenticated I need to clear the old data and make new request with the authentication. My controller gets refreshed but the API call is not being made as the call is already cached. It returns the old data.
You can us a custom cache object using Angular's built-in $cacheFactory
E.g
// cache the HTTP response
$http.get('myurl',{
cache: true
}
// this object can now be retrieved using $cacheFactory
var httpCache = $cacheFactory('$http');
// to remove the value from the cache, get the default $http cache, call the remove function and pass in the url
var httpCache = $cacheFactory.get('$http');
httpCache.remove('myurl');
Telling our $http requests to make requests through our own custom cache is simple. Instead of passing a boolean true with the request, we can pass the instance of the cache.
var myCache = $cacheFactory.get('myCache');
$http({
method: 'GET',
url: '/api/users.json',
cache: myCache
});
// Or, using the .get helper
$http.get('/api/users.json', {
cache: myCache
});
Now, instead of using the default cache, $http will use our custom cache.

Can I create an $http request and not submitting it (yet)?

I have a generic component in my system that deals with submitting HTTP requests in chunks. It takes care of some application-common headers that I need to attach and updating all the necessary places in the GUI.
This component is shared between couple other components for various use-cases. I'm trying to delegate the task of creating HTTP request from a given input to the callers but found out that when the caller does:
var req = $http.get("url", {})
It's already submitting the request.
I'd like the callers to provide a method to generate a list of request objects from the input, and my component will deal with that later (add some headers for example, add success() and error() methods, or submit the requests in batches).
How can I only create the HTTP Request object, without sending it?
You can create wrapper function for "delayed" http request, that would return you preconfigured ready to call function, and use it instead of $http.
Maybe like this:
function dHttp(config) {
return function(options) {
angular.merge(config, options);
return $http(config);
}
}
var req = dHttp({
url: '/some',
data: {test: 21}
});
// later send it
req().then(function(data) {
console.log(data);
});
A plain AJAX can be useful here
xmlhttp.open("GET","a-url",true);
xmlhttp.send();
$http.get() is a declarative form of an ajax open and send.
var reqArr = [];
reqArr.push(xmlhttp.open("GET","a-url",true));
reqArr.push(xmlhttp.open("GET","b-url",true));
reqArr.push(xmlhttp.open("GET","c-url",true));
so you can change the objects as needed and send the ajax later on.

Backbone model's fetch doesn't work when the page initially loads but on second attempt works fine

In my backbone project I am trying to fetch a model based on some search criteria submitted by the users from a form. In submit handler, I am trying to fetch the model by passing search criteria's via data option (following is the code).
var productType=$("#productType").val();
var customerId=$("#customerId").val();
var stateSelected=$("#selectedState").val();
var srbStatus=$("#stateReportingStatus").val();
var dateType=$("#dateType").val();
var fromDate=$("#fromDate").val();
var toDate=$("#toDate").val();
var billTypeInd=$("#billTypeIndicator").val();
var dataElement=$("#dataElement").val();
var ediFileName=$("#ediFileName").val();
var ediBillName=$("#ediBillNumber").val();
var billId=$("#billId").val();
var claimantFirstName=$("#claimantFirstName").val();
var claimantLastName=$("#claimantLastName").val();
var insurerName=$("#insurerName").val();
var insurerFEIN=$("#insurerFEIN").val();
var insurerZip=$("#insurerZIP").val();
var dashboardSearchResultModel= new dashboardSearchResult();
var dashboardSearchResultModel= new dashboardSearchResult();
dashboardSearchResultModel.fetch({
data:{
productType: productType,
customerId: customerId,
state:stateSelected,
srbStatus:srbStatus,
dateType:dateType,
fromDate:fromDate,
toDate:toDate,
billTypeInd:billTypeInd,
dataElement:dataElement,
ediFileName:ediFileName,
ediBillName:ediBillName,
billId:billId,
claimantFirstName:claimantFirstName,
claimantLastName:claimantLastName,
insurerName:insurerName,
insurerFEIN:insurerFEIN,
insurerZip:insurerZip
},
wait:true,
success: function(dashboardSearchResultModel)
{
alert("This is what we get for result"+JSON.stringify(dashboardSearchResultModel));
$('#dashboardResultArea').html(self.dashboardResultTemplate({results:dashboardSearchResultModel.get("results")}));
},
error: function (model, xhr, options) {
alert("Error: An error occurred while trying to fetch the dashboardSearchResultModel");
alert("Error got model"+JSON.stringify(model));
alert("options:"+JSON.stringify(options));
alert("xhr:"+JSON.stringify(xhr));
}
});
Initially when the page loads after providing the search criteria's if I click submit the fetch doesn't work and goes to the error handler. After that when I submit the from second time the fetch works and retrieves data from the backend server. Any idea what is wrong? Thanks in advance.
When error callback is called, it is because your XHTMLRequest to the server returned a error (HTTP status code). So, there is where your problem resides.
Who starts this code? As the erros does not occur on a second attempt, I would suggest that you area callind $('#id').val() when the DOM is not ready. This way you are sending null values to the server, and that's causing the error you are receiving.
To solve your problem, assure you DOM is ready when executing this script.
See if your request is leaving the browser and reaching the server (i.e., cross-domain request fail with status 0, not reaching the server).
And, if it is, debug your server-side, as it does not seem to be an client-side problem.
So after trying many things I finally decided to try $.ajax call rather the fetch method. This is what I came up with
$.ajax({
type: "GET",
url: "rest/dashboardResult",
dataTyp: 'json',
data: {
productType: productType,
customerId: customerId,
state:stateSelected,
srbStatus:srbStatus,
dateType:dateType,
fromDate:fromDate,
toDate:toDate,
billTypeInd:billTypeInd,
dataElement:dataElement,
ediFileName:ediFileName,
ediBillName:ediBillName,
billId:billId,
claimantFirstName:claimantFirstName,
claimantLastName:claimantLastName,
insurerName:insurerName,
insurerFEIN:insurerFEIN,
insurerZip:insurerZip
}
})
.done(function(response) {
alert( "Result is: " + response);
});
This works without any problem from the get go. Now my question is how to bind the response to the backbone model?
Finally I figured out what was wrong. The call was inside a submit click handler, and $.ajax call or fetch is asynchronous. So by the time the call got reply from the server default action of submit click already took place (which is to reload the page). So by the time success or .done got called the whole page was reloaded. So I put event.preventDefault() at the beginning of handler method and let the handler receive the call back from the server and display it at the template. Thanks everyone for your time.

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