Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / JsonP2.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-data-proxy-JsonP'>/**
19 </span> * @author Ed Spencer
20  *
21  * The JsonP proxy is useful when you need to load data from a domain other than the one your application is running on. If
22  * your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its data
23  * from http://domainB.com because cross-domain ajax requests are prohibited by the browser.
24  *
25  * We can get around this using a JsonP proxy. JsonP proxy injects a `&lt;script&gt;` tag into the DOM whenever an AJAX request
26  * would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag that would be
27  * injected might look like this:
28  *
29  *     &lt;script src=&quot;http://domainB.com/users?callback=someCallback&quot;&gt;&lt;/script&gt;
30  *
31  * When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
32  * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we want
33  * to be notified when the result comes in and that it should call our callback function with the data it sends back. So
34  * long as the server formats the response to look like this, everything will work:
35  *
36  *     someCallback({
37  *         users: [
38  *             {
39  *                 id: 1,
40  *                 name: &quot;Ed Spencer&quot;,
41  *                 email: &quot;ed@sencha.com&quot;
42  *             }
43  *         ]
44  *     });
45  *
46  * As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the JSON
47  * object that the server returned.
48  *
49  * JsonP proxy takes care of all of this automatically. It formats the url you pass, adding the callback parameter
50  * automatically. It even creates a temporary callback function, waits for it to be called and then puts the data into
51  * the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}. Here's how
52  * we might set that up:
53  *
54  *     Ext.define('User', {
55  *         extend: 'Ext.data.Model',
56  *         fields: ['id', 'name', 'email']
57  *     });
58  *
59  *     var store = Ext.create('Ext.data.Store', {
60  *         model: 'User',
61  *         proxy: {
62  *             type: 'jsonp',
63  *             url : 'http://domainB.com/users'
64  *         }
65  *     });
66  *
67  *     store.load();
68  *
69  * That's all we need to do - JsonP proxy takes care of the rest. In this case the Proxy will have injected a script tag
70  * like this:
71  *
72  *     &lt;script src=&quot;http://domainB.com/users?callback=callback1&quot;&gt;&lt;/script&gt;
73  *
74  * # Customization
75  *
76  * This script tag can be customized using the {@link #callbackKey} configuration. For example:
77  *
78  *     var store = Ext.create('Ext.data.Store', {
79  *         model: 'User',
80  *         proxy: {
81  *             type: 'jsonp',
82  *             url : 'http://domainB.com/users',
83  *             callbackKey: 'theCallbackFunction'
84  *         }
85  *     });
86  *
87  *     store.load();
88  *
89  * Would inject a script tag like this:
90  *
91  *     &lt;script src=&quot;http://domainB.com/users?theCallbackFunction=callback1&quot;&gt;&lt;/script&gt;
92  *
93  * # Implementing on the server side
94  *
95  * The remote server side needs to be configured to return data in this format. Here are suggestions for how you might
96  * achieve this using Java, PHP and ASP.net:
97  *
98  * Java:
99  *
100  *     boolean jsonP = false;
101  *     String cb = request.getParameter(&quot;callback&quot;);
102  *     if (cb != null) {
103  *         jsonP = true;
104  *         response.setContentType(&quot;text/javascript&quot;);
105  *     } else {
106  *         response.setContentType(&quot;application/x-json&quot;);
107  *     }
108  *     Writer out = response.getWriter();
109  *     if (jsonP) {
110  *         out.write(cb + &quot;(&quot;);
111  *     }
112  *     out.print(dataBlock.toJsonString());
113  *     if (jsonP) {
114  *         out.write(&quot;);&quot;);
115  *     }
116  *
117  * PHP:
118  *
119  *     $callback = $_REQUEST['callback'];
120  *
121  *     // Create the output object.
122  *     $output = array('a' =&gt; 'Apple', 'b' =&gt; 'Banana');
123  *
124  *     //start output
125  *     if ($callback) {
126  *         header('Content-Type: text/javascript');
127  *         echo $callback . '(' . json_encode($output) . ');';
128  *     } else {
129  *         header('Content-Type: application/x-json');
130  *         echo json_encode($output);
131  *     }
132  *
133  * ASP.net:
134  *
135  *     String jsonString = &quot;{success: true}&quot;;
136  *     String cb = Request.Params.Get(&quot;callback&quot;);
137  *     String responseString = &quot;&quot;;
138  *     if (!String.IsNullOrEmpty(cb)) {
139  *         responseString = cb + &quot;(&quot; + jsonString + &quot;)&quot;;
140  *     } else {
141  *         responseString = jsonString;
142  *     }
143  *     Response.Write(responseString);
144  */
145 Ext.define('Ext.data.proxy.JsonP', {
146     extend: 'Ext.data.proxy.Server',
147     alternateClassName: 'Ext.data.ScriptTagProxy',
148     alias: ['proxy.jsonp', 'proxy.scripttag'],
149     requires: ['Ext.data.JsonP'],
150
151     defaultWriterType: 'base',
152
153 <span id='Ext-data-proxy-JsonP-cfg-callbackKey'>    /**
154 </span>     * @cfg {String} callbackKey
155      * See {@link Ext.data.JsonP#callbackKey}.
156      */
157     callbackKey : 'callback',
158
159 <span id='Ext-data-proxy-JsonP-cfg-recordParam'>    /**
160 </span>     * @cfg {String} recordParam
161      * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString'). Defaults to
162      * 'records'
163      */
164     recordParam: 'records',
165
166 <span id='Ext-data-proxy-JsonP-cfg-autoAppendParams'>    /**
167 </span>     * @cfg {Boolean} autoAppendParams
168      * True to automatically append the request's params to the generated url. Defaults to true
169      */
170     autoAppendParams: true,
171
172     constructor: function(){
173         this.addEvents(
174 <span id='Ext-data-proxy-JsonP-event-exception'>            /**
175 </span>             * @event
176              * Fires when the server returns an exception
177              * @param {Ext.data.proxy.Proxy} this
178              * @param {Ext.data.Request} request The request that was sent
179              * @param {Ext.data.Operation} operation The operation that triggered the request
180              */
181             'exception'
182         );
183         this.callParent(arguments);
184     },
185
186 <span id='Ext-data-proxy-JsonP-method-doRequest'>    /**
187 </span>     * @private
188      * Performs the read request to the remote domain. JsonP proxy does not actually create an Ajax request,
189      * instead we write out a &lt;script&gt; tag based on the configuration of the internal Ext.data.Request object
190      * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
191      * @param {Function} callback A callback function to execute when the Operation has been completed
192      * @param {Object} scope The scope to execute the callback in
193      */
194     doRequest: function(operation, callback, scope) {
195         //generate the unique IDs for this request
196         var me      = this,
197             writer  = me.getWriter(),
198             request = me.buildRequest(operation),
199             params = request.params;
200
201         if (operation.allowWrite()) {
202             request = writer.write(request);
203         }
204
205         // apply JsonP proxy-specific attributes to the Request
206         Ext.apply(request, {
207             callbackKey: me.callbackKey,
208             timeout: me.timeout,
209             scope: me,
210             disableCaching: false, // handled by the proxy
211             callback: me.createRequestCallback(request, operation, callback, scope)
212         });
213
214         // prevent doubling up
215         if (me.autoAppendParams) {
216             request.params = {};
217         }
218
219         request.jsonp = Ext.data.JsonP.request(request);
220         // restore on the request
221         request.params = params;
222         operation.setStarted();
223         me.lastRequest = request;
224
225         return request;
226     },
227
228 <span id='Ext-data-proxy-JsonP-method-createRequestCallback'>    /**
229 </span>     * @private
230      * Creates and returns the function that is called when the request has completed. The returned function
231      * should accept a Response object, which contains the response to be read by the configured Reader.
232      * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
233      * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
234      * theCallback refers to the callback argument received by this function.
235      * See {@link #doRequest} for details.
236      * @param {Ext.data.Request} request The Request object
237      * @param {Ext.data.Operation} operation The Operation being executed
238      * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
239      * passed to doRequest
240      * @param {Object} scope The scope in which to execute the callback function
241      * @return {Function} The callback function
242      */
243     createRequestCallback: function(request, operation, callback, scope) {
244         var me = this;
245
246         return function(success, response, errorType) {
247             delete me.lastRequest;
248             me.processResponse(success, operation, request, response, callback, scope);
249         };
250     },
251
252     // inherit docs
253     setException: function(operation, response) {
254         operation.setException(operation.request.jsonp.errorType);
255     },
256
257
258 <span id='Ext-data-proxy-JsonP-method-buildUrl'>    /**
259 </span>     * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
260      * @param {Ext.data.Request} request The request object
261      * @return {String} The url
262      */
263     buildUrl: function(request) {
264         var me      = this,
265             url     = me.callParent(arguments),
266             params  = Ext.apply({}, request.params),
267             filters = params.filters,
268             records,
269             filter, i;
270
271         delete params.filters;
272
273         if (me.autoAppendParams) {
274             url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
275         }
276
277         if (filters &amp;&amp; filters.length) {
278             for (i = 0; i &lt; filters.length; i++) {
279                 filter = filters[i];
280
281                 if (filter.value) {
282                     url = Ext.urlAppend(url, filter.property + &quot;=&quot; + filter.value);
283                 }
284             }
285         }
286
287         //if there are any records present, append them to the url also
288         records = request.records;
289
290         if (Ext.isArray(records) &amp;&amp; records.length &gt; 0) {
291             url = Ext.urlAppend(url, Ext.String.format(&quot;{0}={1}&quot;, me.recordParam, me.encodeRecords(records)));
292         }
293
294         return url;
295     },
296
297     //inherit docs
298     destroy: function() {
299         this.abort();
300         this.callParent();
301     },
302
303 <span id='Ext-data-proxy-JsonP-method-abort'>    /**
304 </span>     * Aborts the current server request if one is currently running
305      */
306     abort: function() {
307         var lastRequest = this.lastRequest;
308         if (lastRequest) {
309             Ext.data.JsonP.abort(lastRequest.jsonp);
310         }
311     },
312
313 <span id='Ext-data-proxy-JsonP-method-encodeRecords'>    /**
314 </span>     * Encodes an array of records into a string suitable to be appended to the script src url. This is broken out into
315      * its own function so that it can be easily overridden.
316      * @param {Ext.data.Model[]} records The records array
317      * @return {String} The encoded records string
318      */
319     encodeRecords: function(records) {
320         var encoded = &quot;&quot;,
321             i = 0,
322             len = records.length;
323
324         for (; i &lt; len; i++) {
325             encoded += Ext.Object.toQueryString(records[i].data);
326         }
327
328         return encoded;
329     }
330 });
331 </pre>
332 </body>
333 </html>