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