Passing Extra Data to Callbacks

It's common to need to pass extra information to a callback method, but since all callback methods have only one parameter (the return code from the remote method) this can be tricky.

DWR addresses this problem with the addition of several optional attributes on the call options object: arg, callbackArg, and exceptionArg.

For an alternative approach you may use a Javascript closure.

Recommended Method

DWR allows for an argument to be specified and passed to handlers -- eliminating the need for JavaScript closures. This argument can apply to the callbackHandler the exceptionHandler, or both. In order to take advantage of this feature you must use a call options object (call options object is explained in the scripting introduction).

var dataFromBrowser = ...;

var callMetaData = { 
  callback:callbackFunction, 
  arg: dataFromBrowser // specify an argument to pass to the callback and exceptionHandler
};

Remote.method(params, callMetaData);

function callbackFunction(dataFromServer, arg1) {
  // you will now have access to dataFromBrowser as arg1
}
Rules:
  1. arg - If specified will be used as a default argument to pass to the callback and will apply to both callback handlers and exception handlers.
  2. callbackArg - If specified will apply to callback handlers and will override arg.
  3. exceptionArg - If specified will apply to exception handlers and will override arg.
var dataFromBrowser = ...;
var dataFromBrowser2 = ...;

var callMetaData = { 
  callback:callbackFunction, 
  arg: dataFromBrowser, // specify an argument to pass to the exeptionHandler
  callbackArg: dataFromBrowser2, // overrides args will be passed to the callback
  exceptionHandler: exceptionHandlerFunction
};

Remote.method(params, callMetaData);

function callbackFunction(dataFromServer, arg1) {
  // you will now have access to dataFromBrowser2 as arg1
  // callbackArg overrides arg
}

function exceptionHandlerFunction(exceptionMessage, exception, arg1) {
  // you will now have access to dataFromBrowser1 as arg1
  // arg also applies to exceptionHandlers
}

A word about scope

DWR also addresses scope issues within callback and exception handlers. We have added several optional attributes on the call options object: scope, callbackScope, and exceptionScope.

(function() {
    someObject = {};
    someObject.privateVar = "Private variable from the main object."; 

    someObject.callbackFunction = function(dataFromServer) {
      alert(this.privateVar);
      // The preceding line will alert the value of privateVar.  
      // The key here is the use of 'this'.  The scope is not 
      // lost because it is specified in the call options object
      // and used to execute the callback function.
    }   
})();

var callMetaData = { 
  callback:someObject.callbackFunction, 
  scope: someObject 
};

Remote.method(params, callMetaData);
Rules:
  1. scope - If specified will be used as the default scope and will apply to both callback handlers and exception handlers.
  2. callbackScope - If specified will apply to callback handlers and will override scope.
  3. exceptionScope - If specified will apply to exception handlers and will override scope.

Note: The default scope is window.

Alternative Approach - JavaScript Closures

A JavaScript closure is required to pass extra data to a callback or exception handler prior to the 3.0 release. For example, your callback method needs to look something like this:

function callbackFunc(dataFromServer, dataFromBrowser) {
  // do something with dataFromServer and dataFromBrowser ...
}

Then you can arrange for this method to be called as follows:

var dataFromBrowser = ...;

// define an erasure function to store a reference to
// dataFromBrowser and to call dataFromServer
var callbackProxy = function(dataFromServer) {
  callbackFunc(dataFromServer, dataFromBrowser);
};

var callMetaData = { callback:callbackProxy };

Remote.method(params, callMetaData);

(The call options object is explained in the scripting introduction)

In other words the function that you pass as the callback is not the real callback - it's just a closure that acts as a proxy, to pass the data on having added in the client side data.

You could write this more succinctly as:

var dataFromBrowser = ...;
Remote.method(params, {
  callback:function(dataFromServer) {
    callbackFunc(dataFromServer, dataFromBrowser);
  }
});