@qooxdoo/framework
Version:
The JS Framework for Coders
345 lines (281 loc) • 8.94 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2009 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Martin Wittemann (martinwittemann)
* Tristan Koch (tristankoch)
************************************************************************ */
/**
* The JSON data store is responsible for fetching data from an url. The type
* of the data has to be json.
*
* The loaded data will be parsed and saved in qooxdoo objects. Every value
* of the loaded data will be stored in a qooxdoo property. The model classes
* for the data will be created automatically.
*
* For the fetching itself it uses the {@link qx.io.request.Xhr} class and
* for parsing the loaded javascript objects into qooxdoo objects, the
* {@link qx.data.marshal.Json} class will be used.
*
* Please note that if you
*
* * upgrade from qooxdoo 1.4 or lower
* * choose not to force the old transport
* * use a delegate with qx.data.store.IStoreDelegate#configureRequest
*
* you probably need to change the implementation of your delegate to configure
* the {@link qx.io.request.Xhr} request.
*
* This class only needs to be disposed if you want to abort any current I/O
* request
*
*/
qx.Class.define("qx.data.store.Json",
{
extend : qx.core.Object,
/**
* @param url {String|null} The url where to find the data. The store starts
* loading as soon as the URL is give. If you want to change some details
* concerning the request, add null here and set the URL as soon as
* everything is set up.
* @param delegate {Object?null} The delegate containing one of the methods
* specified in {@link qx.data.store.IStoreDelegate}.
*/
construct : function(url, delegate)
{
this.base(arguments);
// store the marshaler and the delegate
this._marshaler = new qx.data.marshal.Json(delegate);
this._delegate = delegate;
if (url != null) {
this.setUrl(url);
}
},
events :
{
/**
* Data event fired after the model has been created. The data will be the
* created model.
*/
"loaded" : "qx.event.type.Data",
/**
* Fired when a parse error (i.e. broken JSON) occurred
* during the load. The data contains a hash of the original
* response and the parser error (exception object).
*/
"parseError" : "qx.event.type.Data",
/**
* Fired when an error (aborted, timeout or failed) occurred
* during the load. The data contains the response of the request.
* If you want more details, use the {@link #changeState} event.
*/
"error" : "qx.event.type.Data"
},
properties :
{
/**
* Property for holding the loaded model instance.
*/
model : {
nullable: true,
event: "changeModel"
},
/**
* The state of the request as an url. If you want to check if the request
* did it’s job, use, the {@link #changeState} event and check for one of the
* listed values.
*/
state : {
check : [
"configured", "queued", "sending", "receiving",
"completed", "aborted", "timeout", "failed"
],
init : "configured",
event : "changeState"
},
/**
* The url where the request should go to.
*/
url : {
check: "String",
apply: "_applyUrl",
event: "changeUrl",
nullable: true
}
},
members :
{
_marshaler : null,
_delegate : null,
__request : null,
// apply function
_applyUrl: function(value, old) {
if (value != null) {
// take care of the resource management
value = qx.util.AliasManager.getInstance().resolve(value);
value = qx.util.ResourceManager.getInstance().toUri(value);
this._createRequest(value);
}
},
/**
* Get request
*
* @return {Object} The request.
*/
_getRequest: function() {
return this.__request;
},
/**
* Set request.
*
* @param request {Object} The request.
*/
_setRequest: function(request) {
this.__request = request;
},
/**
* Creates and sends a GET request with the given url.
*
* Listeners will be added to respond to the request’s "success",
* "changePhase" and "fail" event.
*
* @param url {String} The url for the request.
*/
_createRequest: function(url) {
// dispose old request
if (this.__request) {
this.__request.dispose();
this.__request = null;
}
var req = new qx.io.request.Xhr(url);
this._setRequest(req);
// request json representation
req.setAccept("application/json");
// parse as json no matter what content type is returned
req.setParser("json");
// register the internal event before the user has the change to
// register its own event in the delegate
req.addListener("success", this._onSuccess, this);
req.addListener("parseError", this._onParseError, this);
// check for the request configuration hook
var del = this._delegate;
if (del && qx.lang.Type.isFunction(del.configureRequest)) {
this._delegate.configureRequest(req);
}
// map request phase to it’s own phase
req.addListener("changePhase", this._onChangePhase, this);
// add failed, aborted and timeout listeners
req.addListener("fail", this._onFail, this);
req.send();
},
/**
* Handler called when request phase changes.
*
* Sets the store’s state.
*
* @param ev {qx.event.type.Data} The request’s changePhase event.
*/
_onChangePhase : function(ev) {
var requestPhase = ev.getData(),
requestPhaseToStorePhase = {},
state;
requestPhaseToStorePhase = {
"opened": "configured",
"sent": "sending",
"loading": "receiving",
"success": "completed",
"abort": "aborted",
"timeout": "timeout",
"statusError": "failed"
};
state = requestPhaseToStorePhase[requestPhase];
if (state) {
this.setState(state);
}
},
/**
* Handler called when not completing the request successfully.
*
* @param ev {qx.event.type.Event} The request’s fail event.
*/
_onFail : function(ev) {
var req = ev.getTarget();
this.fireDataEvent("error", req);
},
/**
* Handler called when not completing the request successfully because
* of parse errors.
*
* @param ev {qx.event.type.Data} Hash map containing the original 'request'
* and the original parser 'error' exception object.
*/
_onParseError : function(ev) {
this.fireDataEvent("parseError", ev.getData());
},
/**
* Handler for the completion of the requests. It invokes the creation of
* the needed classes and instances for the fetched data using
* {@link qx.data.marshal.Json}.
*
* @param ev {qx.event.type.Event} The request’s success event.
*/
_onSuccess : function(ev)
{
if (this.isDisposed()) {
return;
}
var req = ev.getTarget(),
data = req.getResponse();
// check for the data manipulation hook
var del = this._delegate;
if (del && qx.lang.Type.isFunction(del.manipulateData)) {
data = this._delegate.manipulateData(data);
}
// create the class
this._marshaler.toClass(data, true);
var oldModel = this.getModel();
// set the initial data
this.setModel(this._marshaler.toModel(data));
// get rid of the old model
if (oldModel && oldModel.dispose) {
oldModel.dispose();
}
// fire complete event
this.fireDataEvent("loaded", this.getModel());
// get rid of the request object
if (this.__request) {
this.__request.dispose();
this.__request = null;
}
},
/**
* Reloads the data with the url set in the {@link #url} property.
*/
reload: function() {
var url = this.getUrl();
if (url != null) {
this._createRequest(url);
}
}
},
/*
*****************************************************************************
DESTRUCT
*****************************************************************************
*/
destruct : function()
{
if (this.__request != null) {
this._disposeObjects("__request");
}
// The marshaler internally uses the singleton pattern
// (constructor.$$instance.
this._disposeSingletonObjects("_marshaler");
this._delegate = null;
}
});