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