From b7e71237d1218eec41ce69e1cf56e5eecdc96bef Mon Sep 17 00:00:00 2001 From: Paul Uithol Date: Wed, 4 Jun 2014 02:44:48 +0200 Subject: [PATCH] API change: make `fetchRelated` return a single promise. Ref #467 --- backbone-relational.js | 4 +-- index.html | 17 ++++++------ test/tests.js | 60 +++++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/backbone-relational.js b/backbone-relational.js index b4f3a81d..2b18d39e 100755 --- a/backbone-relational.js +++ b/backbone-relational.js @@ -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 ) { diff --git a/index.html b/index.html index d18fb843..5a47913f 100644 --- a/index.html +++ b/index.html @@ -517,8 +517,8 @@

Determine the type of collections used for a HasMany relation. If you define a url(models<Backbone.Model[]>) function on the specified collection, this enables - fetchRelated to fetch all missing models in one request, instead of - firing a separate request for each. + fetchRelated to fetch all missing models in one request, + instead of firing a separate request for each.

@@ -793,16 +793,17 @@

fetchRelatedrelationalModel.fetchRelated(key<string>, [options<object>], [refresh<boolean>])

- Returns: deferred[] An array of (zero or more) request objects. + Returns: jQuery.Deferred A jQuery promise

- 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 Backbone.Sync. + 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 refresh 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 options object specifies options to be passed to Backbone.Sync.

- 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 collectionType should have an overridden url diff --git a/test/tests.js b/test/tests.js index 5f07ee0c..c7a8fc87 100644 --- a/test/tests.js +++ b/test/tests.js @@ -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 ); });