Permalink
Browse files

API change: make `fetchRelated` return a single promise. Ref #467

  • Loading branch information...
PaulUithol committed Jun 4, 2014
1 parent 742796a commit b7e71237d1218eec41ce69e1cf56e5eecdc96bef
Showing with 44 additions and 37 deletions.
  1. +2 −2 backbone-relational.js
  2. +9 −8 index.html
  3. +33 −27 test/tests.js
View
@@ -1387,7 +1387,7 @@
* @param {string} key The relation key to fetch models for.
* @param {Object} [options] Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
* @param {Boolean} [refresh=false] Fetch existing models from the server as well (in order to update them).
* @return {jQuery.when[]} An array of request objects.
* @return {jQuery.Deferred} A jQuery promise object
*/
fetchRelated: function( key, options, refresh ) {
// Set default `options` for fetch
@@ -1458,7 +1458,7 @@
}
}
return requests;
return $.when.apply( null, requests );
},
set: function( key, value, options ) {
View
@@ -517,8 +517,8 @@ <h4 class="code">
<p>
Determine the type of collections used for a HasMany relation. If you define a
url(models&lt;Backbone.Model[]&gt;) function on the specified collection, this enables
<a href="#RelationalModel-fetchRelated"><q>fetchRelated</q></a> to fetch all missing models in one request, instead of
firing a separate request for each.
<a href="#RelationalModel-fetchRelated"><q>fetchRelated</q></a> to fetch all missing models in one request,
instead of firing a separate request for each.
</p>
<h4 class="code" id="relations-collectionKey">
@@ -793,16 +793,17 @@ <h4 class="code">
fetchRelated<code>relationalModel.fetchRelated(key&lt;string&gt;, [options&lt;object&gt;], [refresh&lt;boolean&gt;])</code>
</h4>
<p>
Returns: <q>deferred[]</q> An array of (zero or more) request objects.
Returns: <q>jQuery.Deferred</q> A <a href="http://api.jquery.com/category/deferred-object/">jQuery promise</a>
</p>
<p>
Fetch models from the server that were referenced in the model's attributes, but have not been found/created yet.
This can be used specifically for lazy-loading scenarios. Setting update to true guarantees that the model will
be fetched from the server and any model that already exists in the store will be updated with the retrieved data.
The options object specifies options to be passed to <a href="http://backbonejs.org/#Sync">Backbone.Sync</a>.
Fetch the models in a relation from the server. By default, only those models are fetched that were referenced in the attributes,
but have not been found/created yet. This can be used specifically for lazy-loading scenarios.
Setting <q>refresh</q> to true will fetch all model(s) from the server. In that case, any model that already
exists will be updated with the retrieved data.
The <q>options</q> object specifies options to be passed to <a href="http://backbonejs.org/#Sync">Backbone.Sync</a>.
</p>
<p>
By default, a separate request will be fired for each additional model that is to be fetched from the server.
By default, a separate request will be fired for each model that is to be fetched from the server (if `key` references a collection).
However, if your server/API supports it, you can fetch the set of models in one request by specifying a
collectionType for the relation you call fetchRelated on. The <a href="#relations-collectionType"><q>collectionType</q></a>
should have an overridden <a href="http://backbonejs.org/#Collection-url"><q>url</q></a>
View
@@ -959,19 +959,19 @@ $(document).ready(function() {
var idsToFetch = person.getIdsToFetch( 'user' );
deepEqual( idsToFetch, [ 'user-10' ] );
var requests = person.fetchRelated( 'user', { error: function() {
var request = person.fetchRelated( 'user', { error: function() {
errorCount++;
}
});
ok( _.isArray( requests ) );
equal( requests.length, 1, "A request has been made" );
ok( _.isObject( request ) && request.always && request.done && request.fail );
equal( window.requests.length, 1, "A single request has been made" );
ok( person.get( 'user' ) instanceof User );
// Triggering the 'error' callback should destroy the model
requests[ 0 ].error();
// Trigger the 'success' callback to fire the 'destroy' event
window.requests[ window.requests.length - 1 ].success();
window.requests[ 0 ].error();
// Trigger the 'success' callback on the `destroy` call to actually fire the 'destroy' event
_.last( window.requests ).success();
ok( !person.get( 'user' ), "User has been destroyed & removed" );
equal( errorCount, 1, "The error callback executed successfully" );
@@ -981,8 +981,8 @@ $(document).ready(function() {
resource_uri: 'person-11'
});
requests = person2.fetchRelated( 'user' );
equal( requests.length, 0, "No request was made" );
request = person2.fetchRelated( 'user' );
equal( window.requests.length, 1, "No request was made" );
});
test( "fetchRelated on a HasMany relation", function() {
@@ -997,14 +997,16 @@ $(document).ready(function() {
//
// Case 1: separate requests for each model
//
var requests = zoo.fetchRelated( 'animals', { error: function() { errorCount++; } } );
ok( _.isArray( requests ) );
equal( requests.length, 2, "Two requests have been made (a separate one for each animal)" );
window.requests = [];
var request = zoo.fetchRelated( 'animals', { error: function() { errorCount++; } } );
ok( _.isObject( request ) && request.always && request.done && request.fail );
equal( window.requests.length, 2, "Two requests have been made (a separate one for each animal)" );
equal( zoo.get( 'animals' ).length, 3, "Three animals in the zoo" );
// Triggering the 'error' callback for one request should destroy the model
requests[ 0 ].error();
// Trigger the 'success' callback on the `destroy` call to fire the 'destroy' event
window.requests[ 0 ].error();
// Trigger the 'success' callback on the `destroy` call to actually fire the 'destroy' event
_.last( window.requests ).success();
equal( zoo.get( 'animals' ).length, 2, "Two animals left in the zoo" );
@@ -1014,7 +1016,9 @@ $(document).ready(function() {
// Case 2: one request per fetch (generated by the collection)
//
// Give 'zoo' a custom url function that builds a url to fetch a set of models from their ids
window.requests = [];
errorCount = 0;
zoo.get( 'animals' ).url = function( models ) {
return '/animal/' + ( models ? 'set/' + _.pluck( models, 'id' ).join(';') + '/' : '' );
};
@@ -1025,37 +1029,39 @@ $(document).ready(function() {
equal( zoo.get( 'animals' ).length, 1 );
// `fetchRelated` creates two placeholder models for the ids present in the relation.
requests = zoo.fetchRelated( 'animals', { error: function() { errorCount++; } } );
window.requests = [];
request = zoo.fetchRelated( 'animals', { error: function() { errorCount++; } } );
ok( _.isArray( requests ) );
equal( requests.length, 1 );
equal( requests[ 0 ].url, '/animal/set/lion-2;zebra-2/' );
equal( zoo.get('animals').length, 3 );
ok( _.isObject( request ) && request.always && request.done && request.fail );
equal( window.requests.length, 1 );
equal( _.last( window.requests ).url, '/animal/set/lion-2;zebra-2/' );
equal( zoo.get('animals').length, 3, "Three animals in the zoo" );
// Triggering the 'error' callback (some error occured during fetching) should trigger the 'destroy' event
// on both fetched models, but should NOT actually make 'delete' requests to the server!
var numRequests = window.requests.length;
requests[ 0 ].error();
ok( window.requests.length === numRequests, "An error occured when fetching, but no DELETE requests are made to the server while handling local cleanup." );
_.last( window.requests ).error();
equal( window.requests.length, 1, "An error occured when fetching, but no DELETE requests are made to the server while handling local cleanup." );
equal( zoo.get( 'animals' ).length, 1, "Both animals are destroyed" );
equal( errorCount, 2, "The error callback executed successfully for both models" );
// Try to re-fetch; nothing left to get though
requests = zoo.fetchRelated( 'animals' );
window.requests = [];
request = zoo.fetchRelated( 'animals' );
equal( requests.length, 0 );
equal( window.requests.length, 0 );
equal( zoo.get( 'animals' ).length, 1 );
// Re-fetch the existing model
requests = zoo.fetchRelated( 'animals', null, true );
window.requests = [];
request = zoo.fetchRelated( 'animals', null, true );
equal( requests.length, 1 );
equal( requests[ 0 ].url, '/animal/set/monkey-1/' );
equal( window.requests.length, 1 );
equal( _.last( window.requests ).url, '/animal/set/monkey-1/' );
equal( zoo.get( 'animals' ).length, 1 );
// An error while refreshing an existing model shouldn't affect it
requests[ 0 ].error();
window.requests[ 0 ].error();
equal( zoo.get( 'animals' ).length, 1 );
});

0 comments on commit b7e7123

Please sign in to comment.