Skip to content

Latest commit

 

History

History
649 lines (466 loc) · 22.2 KB

api.md

File metadata and controls

649 lines (466 loc) · 22.2 KB

backbone-xhr-events

Do more than what the default Backbone Model/Collection request event does for you. The primary benefits are

  • Provide robust lifecycle events (before-send, after-send, success, error, complete)
  • Emit type specific XHR events to allow for focused binding
  • Give ability to see if a model currently has any pending XHR activity
  • Provide a global event bus to bind to all Model/Collection XHR activity
  • Allow requests to be intercepted to, for example, return cached content
  • Add ability to intercept and override response data before it is returned to the Model/Collection
  • Provide event forwarding capabilities so other objects can simulate XHR activity to match another Model/Collection
  • Make all event names and additional attributes overrideable to meet the needs of your particular project
  • Give external entities a way to observe ajax activity on your Collections and Models

Installation

Browser:

    <script src=".../underscore[-min].js"></script>
    <script src=".../backbone[-min].js"></script>
    <script src=".../backbone-xhr-events[-min].js"></script>

CommonJS

    require('backbone-xhr-events');

AMD

    require(
      ['backbone-xhr-events'], function() {
      // don't do do anything with it... it's initialized now
    });

Sections

General Usage Examples

Listen to all XHR activity

    Backbone.xhrEvents.on('xhr', function(context) {
      context.on('success', function() {
        // context.model has been updated
      });
    });

Override fetch XHR payload or cache it

    Backbone.xhrEvents.on('xhr:read', function(context) {
      context.on('after-send', function(data, status, xhr, responseType) {

        // responseType will either be 'success' or 'error'
        if (responseType === 'success') {

          // wrap the response as a "response" attribute (just to show how to modify it)
          context.data = { response: data };

          // cache the response
          _cacheFetchResponse(JSON.stringify(data), context.xhrSettings.url);
        }
      });
    });

Intercept a fetch request and return a cached payload

    Backbone.xhrEvents.on('xhr:read', function(context) {

      // use the "before-send" event (rather than just doing it here)
      // so we have access to the XHR settings to get the URL
      context.on('before-send', function(xhr, settings) {

        var cachedResult = _getFetchCache(settings.url);
        if (cachedResult) {
          context.preventDefault().success(JSON.parse(cachedResult), 'success');
        }
      });
    });

Make a successful XHR look like a failure

    model.on('xhr', function(context) {

      context.on('after-send', function(data, status, xhr, responseType) {
        if (responseType === 'success') {

          // provide the parameters that you would have wanted coming back directly from the $.ajax callback
          context.preventDefault().error(xhr, 'error', 'Not Found');
        }
      });
    });

Alter the payload asynchronously after the initial response is returned

    Backbone.xhrEvents.on('xhr', function(context) {

      context.on('after-send', function(data, status, xhr) {
        var handler = context.preventDefault();

        yourOwnGetDataAsyncMethod(function(data) {
          handler.success(data, status, xhr);
        });
      });
    });

Add simulated 1 second latency

    Backbone.xhrEvents.on('xhr', function(context) {

      context.on('after-send', function(p1, p2, p3, responseType) {
        var handler = context.preventDefault();

        setTimeout(function() {
          var successOrErrorMethod = handler[responseType];
          successOrErrorMethod(p1, p2, p3);
        }, 1000);
      });
    });

Set a default connection timeout on all XHR activity

    Backbone.xhrEvents.on('xhr', function(context) {
      context.options.timeout = 3000;
    });

Determine fetch status of a model

    model.fetch();
    !!model.xhrActivity === true;

    // model fetch complete now
    !!model.xhrActivity === false;

    // if the model fetch succeeded
    model.hasBeenFetched === true;
    model.hadFetchError === false;

    // if the model fetch has failed...
    model.hadFetchError === true;
    model.hasBeenFetched === false;

Forward xhr events to another model (source model will continue to emit xhr events as well)

    // forward all events
    Backbone.forwardXHREvents(sourceModel, receiverModel);
    // and stop the fowarding
    Backbone.stopXHRForwarding(sourceModel, receiverModel);

    // forward events for a specific Backbone.sync method
    Backbone.forwardXHREvents(sourceModel, receiverModel, 'read');
    // and stop the fowarding
    Backbone.stopXHRForwarding(sourceModel, receiverModel, 'read');

    // forward events *only while the callback function is executed*
    Backbone.forwardXHREvents(sourceModel, receiverModel, function() {
      // any XHR activity that sourceModel executes will be emitted by
      // receiverModel as well
    });

Prevent duplicate concurrent submission of any XHR request

    Backbone.xhrEvents.on('xhr', function(context) {
      var model = context.model,
          method = context.method;

      context.on('before-send', function(xhr, settings) {

        // see if any current XHR activity matches this request
        var match = _.find(model.xhrActivity, function(_context) {
          return _context.xhrSettings.url === settings.url
              && method === _context.method
              && _.isEqual(_context.xhrSettings.data, settings.data);
        });

        if (match) {
          var handler = context.preventDefault();
          // when the pending request comes back, simulate the same activity on this request
          match.on('success', handler.success);
          match.on('error', handler.error);
        }
      });
    });

Prevent the error callback if the request is aborted

    Backbone.xhrEvents.on('xhr', function(context) {
      context.on('abort', function() {
        context.preventDefault().complete('abort');
      });
    });

XHR Method Reference

// create, update, patch, delete, read

  • fetch: read method (xhr:read)
  • destroy: delete method (xhr:destroy)
  • save (new): create method (xhr:create)
  • save (existing): update (xhr:update)
  • save (existing with options {patch: true}): patch (xhr:patch)

Custom Event

this.fetch({event: foo}) -> "xhr:foo"

Overrides

Almost all event names and model/global attributes can be overridden to suit your needs.

  • Backbone.xhrCompleteEventName: the event triggered on a model/collection when all XHR activity has completed (default: xhr:complete)
  • Backbone.xhrModelLoadingAttribute: the model attribute which can be used to return an array of all current XHR request events and determind if the model/collection has current XHR activity (default: xhrActivity)
  • Backbone.xhrEventName: the event triggered on models/collection and the global bus to signal an XHR request (default: xhr)
  • Backbone.xhrGlobalAttribute: global event handler attribute name (on Backbone) used to subscribe to all model xhr events (default: xhrEvents)

RequestContext

The RequestContext is the object provided as the only parameter with the xhr events. It is the context providing the ability to bind to the request lifecycle events.

    model.on('xhr', function(requestContext) { ... });

see the RequestContext events and RequestContext functions and attributes

RequestHandler

The RequestHandler is the response from requestContext.preventDefault(). When preventDefault is called, the request lifecycle is halted. When this happens, you must either call success, error, or complete.

API: Events

RequestContext events

"before-send" (xhr, settings, requestContext)

  • xhr: the XMLHTTPRequest
  • settings: the actual jquery settings object sent by Backbone.sync
  • requestContext: the same object passed to the xhr event. It is provided in case the function does not have access to the closure scope.

Triggered after Backbone.sync has been executed and an XHR object has been created (but before execution).

    model.on('xhr', function(requestContext) {
      requestContext.on('before-send', function(xhr, settings, requestContext) {
        ...
      });
    });

"after-send" ({jquery callback params}, responseType, requestContext)

  • jqueryParams: if success data, status, xhr; if error xhr, status, error; more details
  • responseType: if success success; if error error;
  • requestContext: the same object passed to the xhr event. It is provided in case the function does not have access to the closure scope.

Triggered after the XMLHttpRequest has completed and before the Backbone.sync callback has been executed. This can be used to override the response data or alter the default behavior of the response.

    model.on('xhr', function(requestContext) {
      requestContext.on('after-send', function(p1, p2, p3, responseType, requestContext) {

        if (responseType === 'success') {
          var data = p1, status = p2, xhr = p3;
          // we can change the response payload using requestContext.data
          requestContext.data = {foo: 'bar'};

        } else if (responseType === 'error') {
          var xhr = p1, status = p2, error = p3;
          // or we can completely change the default behavior and make an error look like a success
          requestContext.preventDefault().success({foo: 'bar'}, 'success', xhr);
        }
      });
    });

"success" (data, status, xhr, requestContext)

  • data: the response payload
  • status: the XMLHttpRequest text status
  • xhr: the XMLHttpRequest
  • requestContext: the same object passed to the xhr event. It is provided in case the function does not have access to the closure scope.

Triggered after the success callback handler has executed. This can be used to perform actions after the model has been updated.

    model.on('xhr', function(requestContext) {
      requestContext.on('success', function(data, status, xhr, requestContext) {
        // the updated mode/collection is requestContext.model
      });
    });

"error" (xhr, status, error, requestContext)

  • xhr: the XMLHttpRequest
  • status: the XMLHttpRequest text status
  • error: the error thrown
  • requestContext: the same object passed to the xhr event. It is provided in case the function does not have access to the closure scope.

Triggered after the error callback handler has executed. This can be used to perform actions when an error scenario has occurred.

    model.on('xhr', function(requestContext) {
      requestContext.on('error', function(xhr, status, error, requestContext) {
        ...
      });
    });

"complete" (responseType, requestContext)

  • responseType: if success success; if error error
  • requestContext: the same object passed to the xhr event. It is provided in case the function does not have access to the closure scope.
    model.on('xhr', function(requestContext) {
      requestContext.on('complete', function(type, requestContext) {
        ...
      });
    });

"abort" ()

Executed when the XHR is aborted using RequestContext.abort

    model.on('xhr', function(requestContext) {
      requestContext.on('abort', function() {
        // if we wanted to prevent the error callback when we abort
        requestContext.preventDefault().complete('abort');
      });
    });

Backbone.Model, Backbone.Collection & Backbone.xhrEvents

"xhr" (requestContext, method)

  • method: the Backbone.sync method (by default, read, update, or delete)
  • requestContext: the RequestContext

This event is triggered on any model or collection when any XHR activity is initiated from that model / collection. It is also triggered on Backbone.xhrEvents when XHR activity is initiated from any model / collection.

    model.on('xhr', function(requestContext, method) {
      // the method is also available as requestContext.method

      // for any fetch operations
      if (method === 'read') {
        ...
      }
    });

or, to watch XHR activity from any model or collection

    Backbone.xhrEvents.on('xhr', function(requestContext, method) {
      var theModel = requestContext.model;
      ...
    });

"xhr:{method}" (requestContext)

  • method: the Backbone sync method (by default, read, update, or delete)
  • requestContext: the request context

Emitted when only XHR activity matching the method in the event name occurs

    model.on('xhr:read', function(requestContext) {
      // this is only triggered on model.fetch()
    });

or, to watch XHR activity from any model or collection

    Backbone.xhrEvents.on('xhr:read', function(method, model, context) {
      ...
    });

"xhr:complete" (requestContext)

  • context: the context representing the last XHR activity (see RequestContext)

Triggered on a model or collection (not Backbone.xhrActivity) when any XHR activity has completed and there is no more concurrent XHR activity

API

RequestContext

The RequestContext is the object provided as the only parameter with the xhr events. It is the context providing the ability to bind to the request lifecycle events.

    model.on('xhr', function(requestContext) { ... });

also refer to the RequestContext events

preventDefault ()

Prevent the default execution of the request lifecycle. A ResponseHandler is returned and either the success, error or complete method must be called.

The following example demonstrates preventing an XMLHttpRequest from being created and simulating a success response.

    model.on('xhr', function(requestContext) {
      var responseHandler = requestContext.stopPropogation();
      setTimeout(function() {
        // this can be used asynchronously
        responseHandler.success({foo: 'bar'}, 'success');
      }, 100);
    });

abort ()

Abort the XMLHttpRequest and trigger the abort event.

    model.on('xhr', function(requestContext) {
      // if we need to abort for some reason
      requestContext.abort();
    });

model

The model or collection that initiated the request

    var myModel = ...;
    myModel.on('xhr', function(requestContext) {
      requestContext.model === myModel;
    });

method

The Backbone.sync method. This is either read, update, or delete if the default methods are used.

options

The options provided to the Backbone.ajax method.

  var myModel = ...;
  myModel.on('xhr', function(requestContext) {
    requestContext.options === {foo: 'bar'};
  });
  myModel.fetch({foo: 'bar'});

xhr

The XMLHttpRequest

  model.on('xhr', function(requestContext) {
    // this is a valid object on or after the "before-send" event
    requestContext.xhr;
  });

xhrSettings

The XMLHttpRequest settings containing atributes like the request URL.

  model.on('xhr', function(requestContext) {
    // this is a valid object on or after the "before-send" event
    var url = requestContext.xhrSettings.url;
  });

RequestHandler

The RequestHandler is the response from requestContext.preventDefault(). When preventDefault is called, the request lifecycle is halted. When this happens, you must either call success, error, or complete.

success (data, status, xhr)

Initiate a success response if the preventDefault method was called.

    model.on('xhr', function(requestContext) {
      var requestHandler = requestContext.preventDefault();
      requestHandler.success({foo: 'bar'}, 'success', undefined);
    });

error (xhr, status, error)

Initiate an error response if the preventDefault method was called.

    model.on('xhr', function(requestContext) {
      var requestHandler = requestContext.preventDefault();
      requestHandler.error(undefined, 'error', 'Not Found');
    });

complete (type)

  • type: the completion type; normally success or error but can technically be anything. This will be the value passed to the complete event triggered from the RequestContext.

Using this method will bypass any success / error handlers bound to the XHR request but any completion handlers will still be executed.

    model.on('xhr', function(requestContext) {
      var requestHandler = requestContext.preventDefault();
      requestHandler.complete('abort');
    });

Backbone.Model / Backbone.Collection

whenFetched (successCallback, errorCallback)

  • successCallback: function to be called when the model/collection has been fetched
  • errorCallback: function to be called when if model/collection fetch failed

Initiate a fetch if not already fetching or fetched. Once the model/collection has been fetch, execute the appropriate callback.

    myModel.whenFetched(function(model) {
        // executed when model is fetched (model and myModel are the same)
      },
      function(model) {
        // executed if the model fetch fails
      }
    );

xhrActivity

This is a Model or Collection attribute that can be evaluated as a truthy if there is any current XHR activity.

    var isCurrentXhrActivity = !!model.xhrActivity;

hasBeenFetched

This will be true if the Model or Collection has previously been fetched with a sucessful response. If the Model or Collection is cleared, this value will be reset to undefined.

    var isTheModelPopulated = model.hasBeenFetched;

hadFetchError

This will be true if the Model or Collection encountered an XHR error when performing a fetch. It will reset to false upon a successful fetch operation.

    var wasThereAFetchError = model.hadFetchError;

Backbone

forwardXHREvents (sourceModel, destModel[, method]) or (sourceModel, destModel, autoStopFunc)

  • sourceModel: the originator model of the XHR events
  • destModel: the receiver or proxy of the source model XHR events
  • method: the optional Backbone.sync method to filter the forwarded events
  • autoStopFunc: callback function that, if provided, will stop the forwarding after the function completes execution

Forward XHR events that originate in sourceModel to destModel. These events will also be emitted in sourceModel as well.

This can be useful if you have a composite model containing sub-models and want to aggregate xhr activity to the composite model.

    var CompositeModel = Backbone.Model.extend({
      initialize: function() {
        Backbone.Model.prototype.initialize.apply(this, arguments);

        // when model1 or model2 have xhr activity, "this" will expose the same xhr events
        this.model1 = new Backbone.Model();
        Backbone.forwardXHREvents(this.model1, this);

        this.model2 = new Backbone.Model();
        Backbone.forwardXHREvents(this.model2, this);
      }
    });

stopXHRForwarding (sourceModel, destModel[, method])

  • sourceModel: the originator model of the XHR events
  • destModel: the receiver or proxy of the source model XHR events
  • method: optional Backbone.sync method to filter the forwarded events

Stop forwarding XHR events. This must match a previous forwardXHREvents call.

    Backbone.forwardXHREvents(sourceModel, destModel, 'read');