|
|
Ext.data.proxy Namespace
Download SDK: SharpKit.ExtJs.zip
Classes
|
Ajax
|
AjaxProxy is one of the most widely-used ways of getting data into your application. It uses AJAX requests to
load data from the server, usually to be placed into a Store. Let's take a look at a typical
setup. Here we're going to set up a Store that has an AjaxProxy. To prepare, we'll also set up a
Model: Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'email']
});
//The Store contains the AjaxProxy as an inline configuration
var store = new Ext.data.Store({
model: 'User',
proxy: {
type: 'ajax',
url : 'users.json'
}
});
store.load();
Our example is going to load user data into a Store, so we start off by defining a Model
with the fields that we expect the server to return. Next we set up the Store itself, along with a proxy
configuration. This configuration was automatically turned into an Ext.data.proxy.Ajax instance, with the url we
specified being passed into AjaxProxy's constructor. It's as if we'd done this: new Ext.data.proxy.Ajax({
url: 'users.json',
model: 'User',
reader: 'json'
});
A couple of extra configurations appeared here - model and reader. These are set by default
when we create the proxy via the Store - the Store already knows about the Model, and Proxy's default
Reader is JsonReader. Now when we call store.load(), the AjaxProxy springs into action, making a request to the url we configured
('users.json' in this case). As we're performing a read, it sends a GET request to that url (see actionMethods
to customize this - by default any kind of read will be sent as a GET request and any kind of write will be sent as a
POST request). Limitations AjaxProxy cannot be used to retrieve data from other domains. If your application is running on http://domainA.com
it cannot load data from http://domainB.com because browsers have a built-in security policy that prohibits domains
talking to each other via AJAX. If you need to read data from another domain and can't set up a proxy server (some software that runs on your own
domain's web server and transparently forwards requests to http://domainB.com, making it look like they actually came
from http://domainA.com), you can use Ext.data.proxy.JsonP and a technique known as JSON-P (JSON with
Padding), which can help you get around the problem so long as the server on http://domainB.com is set up to support
JSON-P responses. See JsonPProxy's introduction docs for more details. Readers and Writers AjaxProxy can be configured to use any type of Reader to decode the server's response. If
no Reader is supplied, AjaxProxy will default to using a JsonReader. Reader configuration
can be passed in as a simple object, which the Proxy automatically turns into a Reader
instance: var proxy = new Ext.data.proxy.Ajax({
model: 'User',
reader: {
type: 'xml',
root: 'users'
}
});
proxy.getReader(); //returns an XmlReader instance based on the config we supplied
Url generation AjaxProxy automatically inserts any sorting, filtering, paging and grouping options into the url it generates for
each request. These are controlled with the following configuration options: pageParam - controls how the page number is sent to the server
(see also startParam and limitParam) sortParam - controls how sort information is sent to the server groupParam - controls how grouping information is sent to the server filterParam - controls how filter information is sent to the server Each request sent by AjaxProxy is described by an Operation. To see how we can
customize the generated urls, let's say we're loading the Proxy with the following Operation: var operation = new Ext.data.Operation({
action: 'read',
page : 2
});
Now we'll issue the request for this Operation by calling read: var proxy = new Ext.data.proxy.Ajax({
url: '/users'
});
proxy.read(operation); //GET /users?page=2
Easy enough - the Proxy just copied the page property from the Operation. We can customize how this page data is
sent to the server: var proxy = new Ext.data.proxy.Ajax({
url: '/users',
pagePage: 'pageNumber'
});
proxy.read(operation); //GET /users?pageNumber=2
Alternatively, our Operation could have been configured to send start and limit parameters instead of page: var operation = new Ext.data.Operation({
action: 'read',
start : 50,
limit : 25
});
var proxy = new Ext.data.proxy.Ajax({
url: '/users'
});
proxy.read(operation); //GET /users?start=50&limit=25
Again we can customize this url: var proxy = new Ext.data.proxy.Ajax({
url: '/users',
startParam: 'startIndex',
limitParam: 'limitIndex'
});
proxy.read(operation); //GET /users?startIndex=50&limitIndex=25
AjaxProxy will also send sort and filter information to the server. Let's take a look at how this looks with a
more expressive Operation object: var operation = new Ext.data.Operation({
action: 'read',
sorters: [
new Ext.util.Sorter({
property : 'name',
direction: 'ASC'
}),
new Ext.util.Sorter({
property : 'age',
direction: 'DESC'
})
],
filters: [
new Ext.util.Filter({
property: 'eyeColor',
value : 'brown'
})
]
});
This is the type of object that is generated internally when loading a Store with sorters
and filters defined. By default the AjaxProxy will JSON encode the sorters and filters, resulting in something like
this (note that the url is escaped before sending the request, but is left unescaped here for clarity): var proxy = new Ext.data.proxy.Ajax({
url: '/users'
});
proxy.read(operation); //GET /users?sort=[{"property":"name","direction":"ASC"},{"property":"age","direction":"DESC"}]&filter=[{"property":"eyeColor","value":"brown"}]
We can again customize how this is created by supplying a few configuration options. Let's say our server is set
up to receive sorting information is a format like "sortBy=name#ASC,age#DESC". We can configure AjaxProxy to provide
that format like this: var proxy = new Ext.data.proxy.Ajax({
url: '/users',
sortParam: 'sortBy',
filterParam: 'filterBy',
//our custom implementation of sorter encoding - turns our sorters into "name#ASC,age#DESC"
encodeSorters: function(sorters) {
var length = sorters.length,
sortStrs = [],
sorter, i;
for (i = 0; i We can also provide a custom encodeFilters function to encode our filters.
|
|
AjaxConfig
|
|
|
AjaxEvents
|
|
|
Client
|
Base class for any client-side storage. Used as a superclass for Memory and
Web Storage proxies. Do not use directly, use one of the subclasses instead.
|
|
ClientConfig
|
|
|
ClientEvents
|
|
|
Direct
|
This class is used to send requests to the server using Ext.direct. When a request is made,
the transport mechanism is handed off to the appropriate Provider
to complete the call. Specifying the function This proxy expects a Direct remoting method to be passed in order to be able to complete requests.
This can be done by specifying the directFn configuration. This will use the same direct
method for all requests. Alternatively, you can provide an api configuration. This
allows you to specify a different remoting method for each CRUD action. Parameters This proxy provides options to help configure which parameters will be sent to the server.
By specifying the paramsAsHash option, it will send an object literal containing each
of the passed parameters. The paramOrder option can be used to specify the order in which
the remoting method parameters are passed. Example Usage Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['firstName', 'lastName'],
proxy: {
type: 'direct',
directFn: MyApp.getUsers,
paramOrder: 'id' // Tells the proxy to pass the id as the first parameter to the remoting method.
}
});
User.load(1);
|
|
DirectConfig
|
|
|
DirectEvents
|
|
|
JsonP
|
JsonPProxy is useful when you need to load data from a domain other than the one your application is running
on. If your application is running on http://domainA.com it cannot use Ajax to load its
data from http://domainB.com because cross-domain ajax requests are prohibited by the browser. We can get around this using a JsonPProxy. JsonPProxy injects a <script> tag into the DOM whenever
an AJAX request would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag
that would be injected might look like this: <script src="http://domainB.com/users?callback=someCallback"></script>
When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we
want to be notified when the result comes in and that it should call our callback function with the data it sends
back. So long as the server formats the response to look like this, everything will work: someCallback({
users: [
{
id: 1,
name: "Ed Spencer",
email: "ed@sencha.com"
}
]
});
As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the
JSON object that the server returned. JsonPProxy takes care of all of this automatically. It formats the url you pass, adding the callback
parameter automatically. It even creates a temporary callback function, waits for it to be called and then puts
the data into the Proxy making it look just like you loaded it through a normal AjaxProxy.
Here's how we might set that up: Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'email']
});
var store = new Ext.data.Store({
model: 'User',
proxy: {
type: 'jsonp',
url : 'http://domainB.com/users'
}
});
store.load();
That's all we need to do - JsonPProxy takes care of the rest. In this case the Proxy will have injected a
script tag like this:
<script src="http://domainB.com/users?callback=stcCallback001" id="stcScript001"></script>
Customization Most parts of this script tag can be customized using the callbackParam, callbackPrefix and
scriptIdPrefix configurations. For example:
var store = new Ext.data.Store({
model: 'User',
proxy: {
type: 'jsonp',
url : 'http://domainB.com/users',
callbackParam: 'theCallbackFunction',
callbackPrefix: 'ABC',
scriptIdPrefix: 'injectedScript'
}
});
store.load();
Would inject a script tag like this: <script src="http://domainB.com/users?theCallbackFunction=ABC001" id="injectedScript001"></script>
Implementing on the server side The remote server side needs to be configured to return data in this format. Here are suggestions for how you
might achieve this using Java, PHP and ASP.net: Java: boolean jsonP = false;
String cb = request.getParameter("callback");
if (cb != null) {
jsonP = true;
response.setContentType("text/javascript");
} else {
response.setContentType("application/x-json");
}
Writer out = response.getWriter();
if (jsonP) {
out.write(cb + "(");
}
out.print(dataBlock.toJsonString());
if (jsonP) {
out.write(");");
}
PHP: $callback = $_REQUEST['callback'];
// Create the output object.
$output = array('a' => 'Apple', 'b' => 'Banana');
//start output
if ($callback) {
header('Content-Type: text/javascript');
echo $callback . '(' . json_encode($output) . ');';
} else {
header('Content-Type: application/x-json');
echo json_encode($output);
}
ASP.net: String jsonString = "{success: true}";
String cb = Request.Params.Get("callback");
String responseString = "";
if (!String.IsNullOrEmpty(cb)) {
responseString = cb + "(" + jsonString + ")";
} else {
responseString = jsonString;
}
Response.Write(responseString);
|
|
JsonPConfig
|
|
|
JsonPEvents
|
|
|
LocalStorage
|
The LocalStorageProxy uses the new HTML5 localStorage API to save Model data locally on
the client browser. HTML5 localStorage is a key-value store (e.g. cannot save complex objects like JSON), so
LocalStorageProxy automatically serializes and deserializes data when saving and retrieving it. localStorage is extremely useful for saving user-specific information without needing to build server-side
infrastructure to support it. Let's imagine we're writing a Twitter search application and want to save the user's
searches locally so they can easily perform a saved search again later. We'd start by creating a Search model: Ext.define('Search', {
fields: ['id', 'query'],
extend: 'Ext.data.Model',
proxy: {
type: 'localstorage',
id : 'twitter-Searches'
}
});
Our Search model contains just two fields - id and query - plus a Proxy definition. The only configuration we
need to pass to the LocalStorage proxy is an id. This is important as it separates the Model data in this
Proxy from all others. The localStorage API puts all data into a single shared namespace, so by setting an id we
enable LocalStorageProxy to manage the saved Search data. Saving our data into localStorage is easy and would usually be done with a Store: //our Store automatically picks up the LocalStorageProxy defined on the Search model
var store = new Ext.data.Store({
model: "Search"
});
//loads any existing Search data from localStorage
store.load();
//now add some Searches
store.add({query: 'Sencha Touch'});
store.add({query: 'Ext JS'});
//finally, save our Search data to localStorage
store.sync();
The LocalStorageProxy automatically gives our new Searches an id when we call store.sync(). It encodes the Model
data and places it into localStorage. We can also save directly to localStorage, bypassing the Store altogether: var search = Ext.ModelManager.create({query: 'Sencha Animator'}, 'Search');
//uses the configured LocalStorageProxy to save the new Search to localStorage
search.save();
Limitations If this proxy is used in a browser where local storage is not supported, the constructor will throw an error.
A local storage proxy requires a unique ID which is used as a key in which all record data are stored in the
local storage object. It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided
but the attached store has a storeId, the storeId will be used. If neither option is presented the proxy will
throw an error.
|
|
LocalStorageConfig
|
|
|
LocalStorageEvents
|
|
|
Memory
|
In-memory proxy. This proxy simply uses a local variable for data storage/retrieval, so its contents are lost on
every page refresh. Usually this Proxy isn't used directly, serving instead as a helper to a Store where a
reader is required to load data. For example, say we have a Store for a User model and have some inline data we want
to load, but this data isn't in quite the right format: we can use a MemoryProxy with a JsonReader to read it into
our Store: //this is the model we will be using in the store
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'phone', type: 'string', mapping: 'phoneNumber'}
]
});
//this data does not line up to our model fields - the phone field is called phoneNumber
var data = {
users: [
{
id: 1,
name: 'Ed Spencer',
phoneNumber: '555 1234'
},
{
id: 2,
name: 'Abe Elias',
phoneNumber: '666 1234'
}
]
};
//note how we set the 'root' in the reader to match the data structure above
var store = new Ext.data.Store({
autoLoad: true,
model: 'User',
data : data,
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'users'
}
}
});
|
|
MemoryConfig
|
|
|
MemoryEvents
|
|
|
Proxy
|
Proxies are used by Stores to handle the loading and saving of Model data.
Usually developers will not need to create or interact with proxies directly. Types of Proxy There are two main types of Proxy - Client and Server. The Client proxies
save their data locally and include the following subclasses: LocalStorageProxy - saves its data to localStorage if the browser supports it SessionStorageProxy - saves its data to sessionStorage if the browsers supports it MemoryProxy - holds data in memory only, any data is lost when the page is refreshed The Server proxies save their data by sending requests to some remote server. These proxies include: Ajax - sends requests to a server on the same domain JsonP - uses JSON-P to send requests to a server on a different domain Direct - uses Ext.direct to send requests Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four operations
are mapped to the methods create, read, update and destroy respectively. Each Proxy subclass
implements these functions. The CRUD methods each expect an Operation object as the sole argument. The Operation encapsulates
information about the action the Store wishes to perform, the model instances that are to be modified, etc.
See the Operation documentation for more details. Each CRUD method also accepts a callback function to be
called asynchronously on completion. Proxies also support batching of Operations via a batch object, invoked by the batch method.
|
|
ProxyConfig
|
|
|
ProxyEvents
|
|
|
Rest
|
RestProxy is a specialization of the AjaxProxy which simply maps the four actions
(create, read, update and destroy) to RESTful HTTP verbs. For example, let's set up a Model
with an inline RestProxy Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'email'],
proxy: {
type: 'rest',
url : '/users'
}
});
Now we can create a new User instance and save it via the RestProxy. Doing this will cause the Proxy to send a
POST request to '/users':
var user = Ext.ModelManager.create({name: 'Ed Spencer', email: 'ed@sencha.com'}, 'User');
user.save(); //POST /users
Let's expand this a little and provide a callback for the Ext.data.Model.save call to update the Model
once it has been created. We'll assume the creation went successfully and that the server gave this user an ID of
123: user.save({
success: function(user) {
user.set('name', 'Khan Noonien Singh');
user.save(); //PUT /users/123
}
});
Now that we're no longer creating a new Model instance, the request method is changed to an HTTP PUT, targeting
the relevant url for that user. Now let's delete this user, which will use the DELETE method: user.destroy(); //DELETE /users/123
Finally, when we perform a load of a Model or Store, RestProxy will use the GET method: //1. Load via Store
//the Store automatically picks up the Proxy from the User model
var store = new Ext.data.Store({
model: 'User'
});
store.load(); //GET /users
//2. Load directly from the Model
//GET /users/123
Ext.ModelManager.getModel('User').load(123, {
success: function(user) {
console.log(user.getId()); //outputs 123
}
});
Url generation RestProxy is able to automatically generate the urls above based on two configuration options - appendId
and format. If appendId is true (it is by default) then RestProxy will automatically append the ID of the
Model instance in question to the configured url, resulting in the '/users/123' that we saw above. If the request is not for a specific Model instance (e.g. loading a Store), the url is not appended with an id.
RestProxy will automatically insert a '/' before the ID if one is not already present. new Ext.data.proxy.Rest({
url: '/users',
appendId: true //default
});
// Collection url: /users
// Instance url : /users/123
RestProxy can also optionally append a format string to the end of any generated url: new Ext.data.proxy.Rest({
url: '/users',
format: 'json'
});
// Collection url: /users.json
// Instance url : /users/123.json
If further customization is needed, simply implement the buildUrl method and add your custom generated
url onto the Request object that is passed to buildUrl. See
RestProxy's implementation for an example of
how to achieve this. Note that RestProxy inherits from AjaxProxy, which already injects all of the sorter,
filter, group and paging options into the generated url. See the AjaxProxy docs for more
details.
|
|
RestConfig
|
|
|
RestEvents
|
|
|
Server
|
ServerProxy is a superclass of JsonPProxy and AjaxProxy,
and would not usually be used directly. ServerProxy should ideally be named HttpProxy as it is a superclass for all HTTP proxies - for Ext JS 4.x it has been
called ServerProxy to enable any 3.x applications that reference the HttpProxy to continue to work (HttpProxy is now an
alias of AjaxProxy).
|
|
ServerConfig
|
|
|
ServerEvents
|
|
|
SessionStorage
|
Proxy which uses HTML5 session storage as its data storage/retrieval mechanism.
If this proxy is used in a browser where session storage is not supported, the constructor will throw an error.
A session storage proxy requires a unique ID which is used as a key in which all record data are stored in the
session storage object. It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided
but the attached store has a storeId, the storeId will be used. If neither option is presented the proxy will
throw an error. Proxies are almost always used with a store: new Ext.data.Store({
proxy: {
type: 'sessionstorage',
id : 'myProxyKey'
}
});
Alternatively you can instantiate the Proxy directly: new Ext.data.proxy.SessionStorage({
id : 'myOtherProxyKey'
});
Note that session storage is different to local storage (see Ext.data.proxy.LocalStorage) - if a browser
session is ended (e.g. by closing the browser) then all data in a SessionStorageProxy are lost. Browser restarts
don't affect the Ext.data.proxy.LocalStorage - the data are preserved.
|
|
SessionStorageConfig
|
|
|
SessionStorageEvents
|
|
|
WebStorage
|
WebStorageProxy is simply a superclass for the localStorage and
sessionStorage proxies. It uses the new HTML5 key/value client-side storage
objects to save model instances for offline use.
|
|
WebStorageConfig
|
|
|
WebStorageEvents
|
|
|