Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / data / proxy / Ajax.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @author Ed Spencer
17  *
18  * AjaxProxy is one of the most widely-used ways of getting data into your application. It uses AJAX requests to load
19  * data from the server, usually to be placed into a {@link Ext.data.Store Store}. Let's take a look at a typical setup.
20  * Here we're going to set up a Store that has an AjaxProxy. To prepare, we'll also set up a {@link Ext.data.Model
21  * Model}:
22  *
23  *     Ext.define('User', {
24  *         extend: 'Ext.data.Model',
25  *         fields: ['id', 'name', 'email']
26  *     });
27  *
28  *     //The Store contains the AjaxProxy as an inline configuration
29  *     var store = Ext.create('Ext.data.Store', {
30  *         model: 'User',
31  *         proxy: {
32  *             type: 'ajax',
33  *             url : 'users.json'
34  *         }
35  *     });
36  *
37  *     store.load();
38  *
39  * Our example is going to load user data into a Store, so we start off by defining a {@link Ext.data.Model Model} with
40  * the fields that we expect the server to return. Next we set up the Store itself, along with a
41  * {@link Ext.data.Store#proxy proxy} configuration. This configuration was automatically turned into an
42  * Ext.data.proxy.Ajax instance, with the url we specified being passed into AjaxProxy's constructor.
43  * It's as if we'd done this:
44  *
45  *     new Ext.data.proxy.Ajax({
46  *         url: 'users.json',
47  *         model: 'User',
48  *         reader: 'json'
49  *     });
50  *
51  * A couple of extra configurations appeared here - {@link #model} and {@link #reader}. These are set by default when we
52  * create the proxy via the Store - the Store already knows about the Model, and Proxy's default {@link
53  * Ext.data.reader.Reader Reader} is {@link Ext.data.reader.Json JsonReader}.
54  *
55  * Now when we call store.load(), the AjaxProxy springs into action, making a request to the url we configured
56  * ('users.json' in this case). As we're performing a read, it sends a GET request to that url (see
57  * {@link #actionMethods} to customize this - by default any kind of read will be sent as a GET request and any kind of write
58  * will be sent as a POST request).
59  *
60  * # Limitations
61  *
62  * AjaxProxy cannot be used to retrieve data from other domains. If your application is running on http://domainA.com it
63  * cannot load data from http://domainB.com because browsers have a built-in security policy that prohibits domains
64  * talking to each other via AJAX.
65  *
66  * If you need to read data from another domain and can't set up a proxy server (some software that runs on your own
67  * domain's web server and transparently forwards requests to http://domainB.com, making it look like they actually came
68  * from http://domainA.com), you can use {@link Ext.data.proxy.JsonP} and a technique known as JSON-P (JSON with
69  * Padding), which can help you get around the problem so long as the server on http://domainB.com is set up to support
70  * JSON-P responses. See {@link Ext.data.proxy.JsonP JsonPProxy}'s introduction docs for more details.
71  *
72  * # Readers and Writers
73  *
74  * AjaxProxy can be configured to use any type of {@link Ext.data.reader.Reader Reader} to decode the server's response.
75  * If no Reader is supplied, AjaxProxy will default to using a {@link Ext.data.reader.Json JsonReader}. Reader
76  * configuration can be passed in as a simple object, which the Proxy automatically turns into a {@link
77  * Ext.data.reader.Reader Reader} instance:
78  *
79  *     var proxy = new Ext.data.proxy.Ajax({
80  *         model: 'User',
81  *         reader: {
82  *             type: 'xml',
83  *             root: 'users'
84  *         }
85  *     });
86  *
87  *     proxy.getReader(); //returns an {@link Ext.data.reader.Xml XmlReader} instance based on the config we supplied
88  *
89  * # Url generation
90  *
91  * AjaxProxy automatically inserts any sorting, filtering, paging and grouping options into the url it generates for
92  * each request. These are controlled with the following configuration options:
93  *
94  * - {@link #pageParam} - controls how the page number is sent to the server (see also {@link #startParam} and {@link #limitParam})
95  * - {@link #sortParam} - controls how sort information is sent to the server
96  * - {@link #groupParam} - controls how grouping information is sent to the server
97  * - {@link #filterParam} - controls how filter information is sent to the server
98  *
99  * Each request sent by AjaxProxy is described by an {@link Ext.data.Operation Operation}. To see how we can customize
100  * the generated urls, let's say we're loading the Proxy with the following Operation:
101  *
102  *     var operation = new Ext.data.Operation({
103  *         action: 'read',
104  *         page  : 2
105  *     });
106  *
107  * Now we'll issue the request for this Operation by calling {@link #read}:
108  *
109  *     var proxy = new Ext.data.proxy.Ajax({
110  *         url: '/users'
111  *     });
112  *
113  *     proxy.read(operation); //GET /users?page=2
114  *
115  * Easy enough - the Proxy just copied the page property from the Operation. We can customize how this page data is sent
116  * to the server:
117  *
118  *     var proxy = new Ext.data.proxy.Ajax({
119  *         url: '/users',
120  *         pagePage: 'pageNumber'
121  *     });
122  *
123  *     proxy.read(operation); //GET /users?pageNumber=2
124  *
125  * Alternatively, our Operation could have been configured to send start and limit parameters instead of page:
126  *
127  *     var operation = new Ext.data.Operation({
128  *         action: 'read',
129  *         start : 50,
130  *         limit : 25
131  *     });
132  *
133  *     var proxy = new Ext.data.proxy.Ajax({
134  *         url: '/users'
135  *     });
136  *
137  *     proxy.read(operation); //GET /users?start=50&limit;=25
138  *
139  * Again we can customize this url:
140  *
141  *     var proxy = new Ext.data.proxy.Ajax({
142  *         url: '/users',
143  *         startParam: 'startIndex',
144  *         limitParam: 'limitIndex'
145  *     });
146  *
147  *     proxy.read(operation); //GET /users?startIndex=50&limitIndex;=25
148  *
149  * AjaxProxy will also send sort and filter information to the server. Let's take a look at how this looks with a more
150  * expressive Operation object:
151  *
152  *     var operation = new Ext.data.Operation({
153  *         action: 'read',
154  *         sorters: [
155  *             new Ext.util.Sorter({
156  *                 property : 'name',
157  *                 direction: 'ASC'
158  *             }),
159  *             new Ext.util.Sorter({
160  *                 property : 'age',
161  *                 direction: 'DESC'
162  *             })
163  *         ],
164  *         filters: [
165  *             new Ext.util.Filter({
166  *                 property: 'eyeColor',
167  *                 value   : 'brown'
168  *             })
169  *         ]
170  *     });
171  *
172  * This is the type of object that is generated internally when loading a {@link Ext.data.Store Store} with sorters and
173  * filters defined. By default the AjaxProxy will JSON encode the sorters and filters, resulting in something like this
174  * (note that the url is escaped before sending the request, but is left unescaped here for clarity):
175  *
176  *     var proxy = new Ext.data.proxy.Ajax({
177  *         url: '/users'
178  *     });
179  *
180  *     proxy.read(operation); //GET /users?sort=[{"property":"name","direction":"ASC"},{"property":"age","direction":"DESC"}]&filter;=[{"property":"eyeColor","value":"brown"}]
181  *
182  * We can again customize how this is created by supplying a few configuration options. Let's say our server is set up
183  * to receive sorting information is a format like "sortBy=name#ASC,age#DESC". We can configure AjaxProxy to provide
184  * that format like this:
185  *
186  *      var proxy = new Ext.data.proxy.Ajax({
187  *          url: '/users',
188  *          sortParam: 'sortBy',
189  *          filterParam: 'filterBy',
190  *
191  *          //our custom implementation of sorter encoding - turns our sorters into "name#ASC,age#DESC"
192  *          encodeSorters: function(sorters) {
193  *              var length   = sorters.length,
194  *                  sortStrs = [],
195  *                  sorter, i;
196  *
197  *              for (i = 0; i < length; i++) {
198  *                  sorter = sorters[i];
199  *
200  *                  sortStrs[i] = sorter.property + '#' + sorter.direction
201  *              }
202  *
203  *              return sortStrs.join(",");
204  *          }
205  *      });
206  *
207  *      proxy.read(operation); //GET /users?sortBy=name#ASC,age#DESC&filterBy;=[{"property":"eyeColor","value":"brown"}]
208  *
209  * We can also provide a custom {@link #encodeFilters} function to encode our filters.
210  *
211  * @constructor
212  * Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the Store's call to
213  * {@link Ext.data.Store#load load} will override any specified callback and params options. In this case, use the
214  * {@link Ext.data.Store Store}'s events to modify parameters, or react to loading events.
215  *
216  * @param {Object} config (optional) Config object.
217  * If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make the request.
218  */
219 Ext.define('Ext.data.proxy.Ajax', {
220     requires: ['Ext.util.MixedCollection', 'Ext.Ajax'],
221     extend: 'Ext.data.proxy.Server',
222     alias: 'proxy.ajax',
223     alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],
224     
225     /**
226      * @property {Object} actionMethods
227      * Mapping of action name to HTTP request method. In the basic AjaxProxy these are set to 'GET' for 'read' actions
228      * and 'POST' for 'create', 'update' and 'destroy' actions. The {@link Ext.data.proxy.Rest} maps these to the
229      * correct RESTful methods.
230      */
231     actionMethods: {
232         create : 'POST',
233         read   : 'GET',
234         update : 'POST',
235         destroy: 'POST'
236     },
237     
238     /**
239      * @cfg {Object} headers
240      * Any headers to add to the Ajax request. Defaults to undefined.
241      */
242     
243     /**
244      * @ignore
245      */
246     doRequest: function(operation, callback, scope) {
247         var writer  = this.getWriter(),
248             request = this.buildRequest(operation, callback, scope);
249             
250         if (operation.allowWrite()) {
251             request = writer.write(request);
252         }
253         
254         Ext.apply(request, {
255             headers       : this.headers,
256             timeout       : this.timeout,
257             scope         : this,
258             callback      : this.createRequestCallback(request, operation, callback, scope),
259             method        : this.getMethod(request),
260             disableCaching: false // explicitly set it to false, ServerProxy handles caching
261         });
262         
263         Ext.Ajax.request(request);
264         
265         return request;
266     },
267     
268     /**
269      * Returns the HTTP method name for a given request. By default this returns based on a lookup on
270      * {@link #actionMethods}.
271      * @param {Ext.data.Request} request The request object
272      * @return {String} The HTTP method to use (should be one of 'GET', 'POST', 'PUT' or 'DELETE')
273      */
274     getMethod: function(request) {
275         return this.actionMethods[request.action];
276     },
277     
278     /**
279      * @private
280      * TODO: This is currently identical to the JsonPProxy version except for the return function's signature. There is a lot
281      * of code duplication inside the returned function so we need to find a way to DRY this up.
282      * @param {Ext.data.Request} request The Request object
283      * @param {Ext.data.Operation} operation The Operation being executed
284      * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
285      * passed to doRequest
286      * @param {Object} scope The scope in which to execute the callback function
287      * @return {Function} The callback function
288      */
289     createRequestCallback: function(request, operation, callback, scope) {
290         var me = this;
291         
292         return function(options, success, response) {
293             me.processResponse(success, operation, request, response, callback, scope);
294         };
295     }
296 }, function() {
297     //backwards compatibility, remove in Ext JS 5.0
298     Ext.data.HttpProxy = this;
299 });
300