WCF 2.x provides a nice and convenient way to issue AJAX requests via WCF.Action.Proxy which is a wrapper around jQuery's $.ajax itself. While it does work smoothly and offer a consistent way to accomplish this, it requires a bit too much effort to properly use it within a class. We've already dove into some changes that will take place with WCF 2.2 and it is about time for some real world examples to showcase you the strength of the new API.
Lets consider a typical class that can be seen in WCF 2.0/2.1:
WCF.Foo.Bar = Class.extend({
_proxy: null,
init: function() {
this._proxy = new WCF.Action.Proxy({
success: $.proxy(this._success, this)
});
$('.someButtonsWithIDs').click($.proxy(this._click, this));
},
_click: function(event) {
this._proxy.setOption('data', {
actionName: 'foo',
className: 'wcf\\data\\foo\\bar\\FooBarAction',
objectIDs: [ $(event.currentTarget).data('someID') ]
});
this._proxy.sendRequest();
},
_success: function(data) {
// yada yada yada
}
});
Display More
This example shows three major downsides of the current API:
- In line 2 we need to define the property _proxy (we could define it on the fly within init(), but extending the object on runtime is rather expensive!)
- Lines 5-7 create a new instance of WCF.Action.Proxy which is kind of bad because we're spawning that instance regardless if one of the click events are ever invoked
- In line 14 and 15 we need to define actionName and most important className which usually do not change within the class
We all know that developers are lazy (and so are we), thus we built a new API which circumvents all these downsides by utilizing a lazy factory to do the job.
Lets look at a sample module definition:
require(['Ajax'], function(Ajax) {
"use strict";
function FooBar() {};
FooBar.prototype = {
setup: function() {
var buttons = document.getElementsByClassName('someButtonsWithIDs');
for (var i = 0, length = buttons.length; i < length; i++) {
buttons[i].addEventListener('click', this._click.bind(this));
}
},
_click: function(event) {
Ajax.api(this, {
objectIDs: [ event.currentTarget.getAttribute('data-some-id') ]
});
},
_ajaxSetup: function() {
return {
data: {
actionName: 'foo',
className: 'wcf\\data\\foo\\bar\\FooBarAction'
}
};
},
_ajaxSuccess: function(data) {
// yada yada yada
}
};
return new FooBar();
});
Display More
There are three major differences:
- We've at no point defined an instance of the Ajax class
- We've never bound any methods in first place
- The _click-event in line 14-16 does not set anything else except the data we actually want to transmit
The important point lies in line 14: Ajax.api(this, …. The Ajax class uses the first argument to store an internal request object which is re-used every time for performance reasons, this is pretty much the same as this._proxy = new WCF.Action.Proxy(…);. In addition it uses the _ajaxSetup() method which returns the initialization data including the special key data. But wait, let's go through this step by step!
We've eliminated the need to statically define an AJAX request object, the first call to Ajax.api() will implicitly spawn a request object at the second you actually need it. All data that needs to be set to construct a request object are taken from the return value of _ajaxSetup, this means your methods do not have to care if the request object was already created, it will always be there if they attempt to call it. In addition there are a few methods that are automatically called if they are present, even though none of them need to be actually implemented:
- _ajaxFailure() handles failed requests, similar to the failure callback of WCF.Action.Proxy
- _ajaxFinalize() is invoked once the request has been completed, regardless whether it failed or succeeded, similar to the after callback of WCF.Action.Proxy
- _ajaxSuccess() handles successful request, similar to the success callback of WCF.Action.Proxy
The only requirement for an object is to implement _ajaxSetup(), everything else is optional and you can even make _ajaxSetup() return an empty object if you wish to.
There is one really cool thing I would like to highlight at this point: The data key of the object returned by _ajaxSetup(). The value will be automatically "pinned", which means it will be added to the data that is sent to the server with every request made. You don't need to specify the className over and over, the Ajax class will automatically fill in that bit and save you from writing the same lines over and over again. Yet, these "pinned" values are considered weak, they can be overridden if you explicitly provide them when calling Ajax.api()!
All these changes should help you to build efficient and fast code without caring to much what happens under the hood, it is the framework's responsibility to take care of it. Oh, and bonus points for having an API that does not require any framework whatsoever, it is pure vanilla JavaScript.
By the way, you can also directly require AjaxRequest and work with the plain request object, see https://github.com/WoltLab/WCF…F/Ajax/Request.js#L36-L53 for the full list of available options.